Какие функции оболочки стандартной библиотеки C ++ вы используете? - PullRequest
81 голосов
/ 31 марта 2010

Этот вопрос , заданный сегодня утром, заставил меня задуматься о том, какие функции, по вашему мнению, отсутствуют в стандартной библиотеке C ++, и как вы справились с заполнением пробелов функциями-обертками. Например, моя собственная служебная библиотека имеет эту функцию для векторного добавления:

template <class T>
std::vector<T> & operator += ( std::vector<T> & v1,
                               const std::vector <T> & v2 ) {
    v1.insert( v1.end(), v2.begin(), v2.end() );
    return v1;
}

и этот для очистки (более или менее) любого типа - особенно полезен для таких вещей, как std :: stack:

template <class C>
void Clear( C & c ) {
    c = C();
}

У меня есть еще несколько, но меня интересуют, какие вы используете? Пожалуйста, ограничьте ответы функциями wrapper - т.е. не более пары строк кода.

Ответы [ 26 ]

36 голосов
/ 31 марта 2010

повышение :: массив

содержит (контейнер, val) (довольно просто, но удобно).

template<typename C, typename T>
bool contains(const C& container, const T& val) {
   return std::find(std::begin(container), std::end(container), val) != std::end(container);
}

remove_unstable (начало, конец, значение)

Более быстрая версия std :: remove за исключением того, что она не сохраняет порядок оставшихся объектов.

template <typename T> 
T remove_unstable(T start, T stop, const typename T::value_type& val){  
    while(start != stop) {      
        if (*start == val) {            
            --stop;             
            ::std::iter_swap(start, stop);      
        } else {            
            ++start;        
        }   
    }   
    return stop; 
}

(в случае вектора типов модулей (int, float и т. Д.) И почти все объекты удаляются, std :: remove может быть быстрее)

35 голосов
/ 31 марта 2010

Довольно часто я использовал бы вектор как набор элементов в произвольном порядке (и, очевидно, когда мне не нужны быстрые проверки «это этот элемент в наборе»). В этих случаях вызов erase () - пустая трата времени, поскольку он переупорядочивает элементы, а меня не волнует порядок. Вот когда функция O (1) ниже пригодится - просто переместите последний элемент в положение, которое вы хотите удалить:

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}
26 голосов
/ 31 марта 2010
template < class T >
class temp_value {
    public :
        temp_value(T& var) : _var(var), _original(var) {}
        ~temp_value()        { _var = _original; }
    private :
        T&  _var;
        T   _original;
        temp_value(const temp_value&);
        temp_value& operator=(const temp_value&);
};

Хорошо, поскольку кажется, что это не так просто, как я думал, вот объяснение:
В своем конструкторе temp_value хранится ссылка на переменную и копия исходного значения переменной. В своем деструкторе он восстанавливает указанную переменную в исходное значение. Таким образом, независимо от того, что вы сделали с переменной между созданием и разрушением, она будет сброшена, когда объект temp_value выйдет из области видимости.
Используйте это так:

void f(some_type& var)
{
  temp_value<some_type> restorer(var); // remembers var's value

  // change var as you like
  g(var);

  // upon destruction restorer will restore var to its original value
}

Вот еще один подход, использующий трюк прицеливания:

namespace detail
{
    // use scope-guard trick
    class restorer_base
    {
    public:
        // call to flag the value shouldn't
        // be restored at destruction
        void dismiss(void) const
        {
            mDismissed = true;
        }

    protected:
        // creation
        restorer_base(void) :
        mDismissed(false) 
        {}

        restorer_base(const restorer_base& pOther) :
        mDismissed(pOther.is_dismissed())
        {
            // take "ownership"
            pOther.dismiss();
        }

        ~restorer_base(void) {} // non-virtual

        // query
        bool is_dismissed(void) const
        {
            return mDismissed;
        }

    private:
        // not copy-assignable, copy-constructibility is ok
        restorer_base& operator=(const restorer_base&);

        mutable bool mDismissed;
    };

    // generic single-value restorer, could be made 
    // variadic to store and restore several variables
    template <typename T>
    class restorer_holder : public restorer_base
    {
    public:
        restorer_holder(T& pX) :
        mX(pX),
        mValue(pX)
        {}

        ~restorer_holder(void)
        {
            if (!is_dismissed())
                mX = mValue;
        }

    private:
        // not copy-assignable, copy-constructibility is ok
        restorer_holder& operator=(const restorer_holder&);

        T& mX;
        T mValue;
    };
}

// store references to generated holders
typedef const detail::restorer_base& restorer;

// generator (could also be made variadic)
template <typename T>
detail::restorer_holder<T> store(T& pX)
{
    return detail::restorer_holder<T>(pX);
}

Это просто немного больше кода, но позволяет более чистое использование:

#include <iostream>

template <typename T>
void print(const T& pX)
{
    std::cout << pX << std::endl;
}

void foo(void)
{
    double d = 10.0;
    double e = 12.0;
    print(d); print(e);

    {
        restorer f = store(d);
        restorer g = store(e);

        d = -5.0;
        e = 3.1337;
        print(d); print(e);

        g.dismiss();
    }

    print(d); print(e);
}

int main(void)
{
    foo();

    int i = 5;
    print(i);

    {
        restorer r = store(i);

        i *= 123;
        print(i);
    }

    print(i);
}

Тем не менее, он исключает возможность использования в классе.


Вот третий способ достижения того же эффекта (который не страдает от проблем с потенциальным бросанием деструкторов):

Реализация:

//none -- it is built into the language

Использование:

#include <iostream>

template <typename T>
void print(const T& pX)
{
    std::cout << pX << std::endl;
}

void foo(void)
{
    double d = 10.0;
    double e = 12.0;
    print(d); print(e);

    {
        double f(d);
        double g(e);

        f = -5.0;
        g = 3.1337;
        print(f); print(g);

        e = std::move(g);
    }

    print(d); print(e);
}

int main(void)
{
    foo();

    int i = 5;
    print(i);

    {
        int r(i);

        r *= 123;
        print(r);
    }

    print(i);
}
22 голосов
/ 31 марта 2010

Не совсем фантик, но скандально пропавший copy_if. С здесь

template<typename In, typename Out, typename Pred>
Out copy_if(In first, In last, Out res, Pred Pr)
{
    while (first != last) {
        if (Pr(*first)) {
            *res++ = *first;
        }
        ++first;
    }
    return res;
}
18 голосов
/ 31 марта 2010
template< typename T, std::size_t sz >
inline T* begin(T (&array)[sz]) {return array;}

template< typename T, std::size_t sz >
inline T* end  (T (&array)[sz]) {return array + sz;}
12 голосов
/ 02 апреля 2010

Иногда мне кажется, что я в аду begin() и end(). Я хотел бы иметь некоторые функции, такие как:

template<typename T>
void sort(T& x)
{
    std::sort(x.begin(), x.end());
}

и другие подобные для std::find, std::for_each и, в основном, для всех алгоритмов STL.

Я чувствую, что sort(x) гораздо быстрее читать / понимать, чем sort(x.begin(), x.end()).

9 голосов
/ 31 марта 2010

Функция полезности в любом наборе инструментов, конечно, copy_if. Хотя на самом деле это не обертка.

Другим часто используемым помощником является deleter, функтор, который я использую с std::for_each для удаления всех указателей в контейнере.

[править] Копаясь в моем "sth.h" я также нашел vector<wstring> StringSplit(wstring const&, wchar_t);

9 голосов
/ 01 апреля 2010

У меня есть заголовок, который помещает следующее в пространство имен «util»:

// does a string contain another string
inline bool contains(const std::string &s1, const std::string &s2) {
    return s1.find(s2) != std::string::npos;
}

// remove trailing whitespace
inline std::string &rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// remove leading whitespace
inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// remove whitespace from both ends
inline std::string &trim(std::string &s) {
    return ltrim(rtrim(s));
}

// split a string based on a delimeter and return the result (you pass an existing vector for the results)
inline std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

// same as above, but returns a vector for you
inline std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    return split(s, delim, elems);
}

// does a string end with another string
inline bool endswith(const std::string &s, const std::string &ending) {
    return ending.length() <= s.length() && s.substr(s.length() - ending.length()) == ending;
}

// does a string begin with another string  
inline bool beginswith(const std::string &s, const std::string &start) {
    return s.compare(0, start.length(), start) == 0;
}
9 голосов
/ 31 марта 2010

Я больше этим не пользуюсь, но раньше он был основным:

template<typename T>
std::string make_string(const T& data) {
    std::ostringstream stream;
    stream << data;
    return stream.str();
}

Буду обновлять больше, как я их помню. : P

8 голосов
/ 31 марта 2010

Алгоритм безнравственного пропажи erase:

  template <
    class Container,
    class Value
    >
  void erase(Container& ioContainer, Value const& iValue)
  {
    ioContainer.erase(
      std::remove(ioContainer.begin(),
                  ioContainer.end(),
                  iValue),
       ioContainer.end());
  } // erase

  template <
    class Container,
    class Pred
    >
  void erase_if(Container& ioContainer, Pred iPred)
  {
    ioContainer.erase(
      std::remove_if(ioContainer.begin(),
                     ioContainer.end(),
                     iPred),
       ioContainer.end());
  } // erase_if
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...