Какие функции оболочки стандартной библиотеки C ++ вы используете?

81

Этот вопрос , заданный сегодня утром, заставил меня задуматься, какие функции, по вашему мнению, отсутствуют в стандартной библиотеке 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();
}

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

мин
источник
9
Считается ли, что я обернул большую часть алгоритма STL для работы с целыми контейнерами, а не с диапазонами, просто потому, что возиться с итераторами - это обычная ошибка :)?
Matthieu M.
2
@ Билли, на самом деле, CW - не повод задавать субъективные вопросы. Я изменю название, чтобы люди остались довольны.
4
@kts: Поскольку vector :: insert получает итераторы с произвольным доступом, хорошая реализация будет использовать диспетчеризацию во время компиляции, чтобы сделать это сама.
4
А не лучше написать c.swap(C())для очистки контейнера?
Alexandre C.
5
@Alexandre: Это запрещено: он привязывает временное значение к неконстантной ссылке. C (). Swap (c) будет работать.

Ответы:

37

boost :: array

contains (container, 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; 
}

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

Виктор Зер
источник
4
Кто-нибудь еще думает, что contains нуждается в третьем шаблоне ( bool sorted=false) и специализации, когда sorted==trueвызывать binary_searchвместо find?
KitsuneYMG
6
@kts: когда вы знаете, что контейнер отсортирован, просто вызовите binary_search напрямую.
2
boost :: array Эквивалент STL доступен в пространстве имен tr1 в самых последних компиляторах (даже codewarrior): std :: tr1 :: array <>
Klaim
36

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

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}
сбк
источник
Хороший. Не думал о том, чтобы сделать это вот так ... :) Должна быть обертка «Сумка» (похожая на стек и очередь), которая ускоряет эти операции, когда порядок не важен.
Macke
12
А в C ++ 0x v[index] = st::move(v.back()); v.pop_back();он настолько эффективен, насколько это возможно.
GManNickG
@Matthieu: Посмотрите на отметку времени на них. : PI заметил пару часов спустя, намного больше разрешенного времени редактирования.
GManNickG 01
@GMan: ты уверен в этом? мне кажется, что v.pop_back () может привести к неопределенному поведению при вызове деструктора.
Виктор Сер
1
@Viktor: Семантика перемещения не означает «я беру ваши ресурсы и все», они означают «я беру ваши ресурсы и помещаю вас в состояние отсутствия ресурсов». Другими словами, ваша семантика перемещения должна гарантировать, что объект может безопасно разрушиться; довольно просто сделать, просто установите указатели на ноль.
GManNickG
26
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);
}
GManNickG
источник
1
@Billy: Это для автоматического восстановления значения позже. (И val должен быть исключен из ctor.)
Извините, я все еще заблудился (я новичок в C ++), может ли кто-нибудь сделать это сразу?
dreamlax
1
@dreamlax: Я добавил к ответу некоторый описательный текст, описывающий это. Теперь это понятно, или мне стоит углубиться в детали?
sbi
1
о, второй довольно умный.
jalf
1
Хм, как это можно использовать в реальной жизни?
Paulm
22

Не совсем обертка, но печально известного пропала 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;
}
Glen
источник
2
Не отвечает на вопрос, не обертка для stdlib.
10
@Roger Pate, да, я знаю, поэтому ответ начинается со слов «Не совсем обертка, но ....».
Glen
1
Детали реализации @Roger. Если вы действительно хотите, вы можете реализовать это в терминах remove_copy_if(). : p
wilhelmtell
18
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;}
оборота SBI
источник
2
Они у меня тоже есть. :) +1 Как бы то ни было, нужно всего два (отбросьте константные версии). Когда массив имеет значение const, Tбудет const Uи вы получите намеченную функцию.
GManNickG
@GMan: Была какая-то версия GCC, которая не компилировала код только с не- constверсиями, поэтому constверсии есть. Поскольку это могло быть ошибкой конкретной версии GCC, я удалю их.
sbi
1
@Marcus: Они намного старше Boost.Range. :)
sbi
4
@Roger: он обертывает массивы для использования со стандартной библиотекой. Вот так. :)
sbi 02
2
@Stacked, @sbe: массивы никогда не передаются по значению, независимо от того, есть ли &. Здесь &можно включить вычитание типа длины массива.
Mankarse 03
12

Иногда я чувствую, что я в 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()).

Обратный
источник
1
подсказка; используйте вместо этого ´sort (boost :: begin (x), boost: end (x)); ´, и вы также сможете сортировать массивы.
Виктор Сер
4
Boost.Range v2 имеет такие адаптеры для всей стандартной библиотеки.
ildjarn
9

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

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

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

Джон Парди
источник
3
Хе-хе - своего рода ярлык boost::lexical_cast<t, t>.
Билли Онил
1
да, это замечательно, если вы не хотите вкладывать деньги в проект,
Стив
11
@BillyONeal: Вот почему я им больше не пользуюсь. @ Стив: Вот почему я до сих пор его использую.
Джон Парди
Было бы неоправданно дорого называть это а char*или а std::string. Может быть, у вас есть специализация шаблонов?
Wilhelmtell
Если не ошибаюсь, boost::lexical_castесть куча таких специализаций и проверок ошибок. Однако для преобразования нечетного числа это работает нормально.
Джон Парди
9

Утилита в наборе инструментов есть, конечно copy_if. Хотя не совсем обертка.

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

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

оборота MSalters
источник
+1 для функторов удаления. Мой функтор удаления хорошо работает с большинством контейнеров, однако я пытался заставить его работать с std :: map, где либо ключ, либо значение являются указателем. Я попытался использовать черты типов для решения проблемы, но не очень далеко продвинулся из-за нехватки времени. Вы решили эту проблему или считаете это проблемой?
Glen
2
@Matthieu M. К сожалению, не все из нас могут использовать Boost.
Glen
@Glen: Все эти проблемы можно решить с помощью умных указателей, желательно (но не обязательно) от Boost. В качестве важного побочного эффекта контейнеры с указателями на динамически выделяемые объекты также внезапно становятся безопасными в отношении исключений.
sbi
@Glen здесь, здесь для проектов, которые не предусматривают никаких библиотек, кроме STD, вплоть до Boost или TR1.
Wheaties 02
7
@ несчастные жертвы корпоративной глупости: получите исключение для собственных библиотек, а затем импортируйте полезные части Boost во внутреннюю библиотеку. Даже в политике все проблемы можно решить с помощью другого уровня косвенности.
MSalters
9

У меня есть заголовок, который помещает следующее в пространство имен "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;
}
Эван Теран
источник
2
Это split()проглатывает любые возникающие ошибки std::getline()и молча возвращает слишком короткий вектор.
SBI 01
конечно, вы должны проверить size()результат перед извлечением ваших строк.
Эван Теран
И как мне узнать, сколько строк должно быть в результате?
sbi 01
2
@sbi: Ваш комментарий вызвал у меня интерес к тому, что на самом деле может пойти не так (кроме строки, просто не имеющей достаточного количества токенов для получения) с циклом stringstream/ getline. Я задал вопрос об этом здесь: stackoverflow.com/questions/2562906/…
Эван Теран
2
@ Эван: Я поправляюсь. См. Мой комментарий на stackoverflow.com/2563542#2563542 . Сожалею.
sbi 02
8

Печально известный отсутствующий 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
Матье М.
источник
+1, собирался опубликовать мой точный эквивалент. Хотя я назвал его remove_erase (...)
Виктор Сер
2
Единственная проблема с этим состоит в том, что он нарушает семантическую идиому удаления-удаления, ожидаемую в STL. Идиома удаления-удаления нужна с любым алгоритмом, имеющим семантику удаления, а не только с std::remove. Например, std::unique.
Билли Онил
Что ж, у меня есть контейнеры, адаптированные для большинства алгоритмов STL для этого :) Но это тот, который я использую чаще всего, обычно, если мне нужна уникальность, я использую setдля начала.
Matthieu M.
@Matthieu M .: Просто имейте в виду, делая это, у людей, которые работают с STL, все время будут звучать тревожные звонки в головах «ВНИМАНИЕ: УДАЛИТЬ АЛГОРИТМ БЕЗ ПРИЗЫВОВ ДЛЯ УДАЛЕНИЯ !!» . В этом нет ничего плохого, но я бы не стал делать это, если бы мне нужно было поделиться своим кодом между многими программистами. Только мои 2 цента.
Билли Онил
1
@Billy: вот почему я назвал это стиранием, а не удалением. Я мало что могу сделать, кроме как разрешить им ознакомиться с кодом. К счастью, с современной IDE определение находится на расстоянии одного клика :)
Matthieu M.
7

Обертывание sprintf

string example = function("<li value='%d'>Buffer at: 0x%08X</li>", 42, &some_obj);
// 'function' is one of the functions below: Format or stringf

Цель состоит в том, чтобы отделить форматирование от вывода без проблем с sprintf и ему подобными. Это некрасиво, но очень полезно, особенно если ваши правила кодирования запрещают iostreams.


Вот версия, которая выделяет по мере необходимости, от Нила Баттерворта. [Просмотреть историю изменений версии Майка, которую я удалил как часть оставшихся двух. Он похож на метод Нила, за исключением того, что последний безопасен в отношении исключений за счет использования вектора вместо delete []: строковый ctor выдаст ошибку при распределении. Майк также использует ту же технику, показанную позже, для определения размера заранее. –RP]

string Format( const char * fmt, ... ) {
  const int BUFSIZE = 1024;
  int size = BUFSIZE, rv = -1;
  vector <char> buf;
  do {
    buf.resize( size );
    va_list valist;
    va_start( valist, fmt );
    // if _vsnprintf() returns < 0, the buffer wasn't big enough
    // so increase buffer size and try again
    // NOTE: MSFT's _vsnprintf is different from C99's vsnprintf,
    //       which returns non-negative on truncation
    //       http://msdn.microsoft.com/en-us/library/1kt27hek.aspx
    rv = _vsnprintf( &buf[0], size, fmt, valist );
    va_end( valist );
    size *= 2;
  }
  while( rv < 0 );
  return string( &buf[0] );
}

Вот версия, которая заранее определяет необходимый размер, от Roger Pate . Для этого требуются записываемые std :: strings, которые предоставляются популярными реализациями, но явно требуются C ++ 0x. [Просмотреть историю изменений версии Маркуса, которую я удалил, так как она немного отличается, но по сути является подмножеством приведенной ниже. –RP]

Реализация

void vinsertf(std::string& s, std::string::iterator it,
             char const* fmt, int const chars_needed, va_list args
) {
  using namespace std;
  int err; // local error code
  if (chars_needed < 0) err = errno;
  else {
    string::size_type const off = it - s.begin(); // save iterator offset
    if (it == s.end()) { // append to the end
      s.resize(s.size() + chars_needed + 1); // resize, allow snprintf's null
      it = s.begin() + off; // iterator was invalidated
      err = vsnprintf(&*it, chars_needed + 1, fmt, args);
      s.resize(s.size() - 1); // remove snprintf's null
    }
    else {
      char saved = *it; // save char overwritten by snprintf's null
      s.insert(it, chars_needed, '\0'); // insert needed space
      it = s.begin() + off; // iterator was invalidated
      err = vsnprintf(&*it, chars_needed + 1, fmt, args);
      *(it + chars_needed) = saved; // restore saved char
    }

    if (err >= 0) { // success
      return;
    }
    err = errno;
    it = s.begin() + off; // above resize might have invalidated 'it'
    // (invalidation is unlikely, but allowed)
    s.erase(it, it + chars_needed);
  }
  string what = stringf("vsnprintf: [%d] ", err);
  what += strerror(err);
  throw runtime_error(what);
}

Публичный интерфейс

std::string stringf(char const* fmt, ...) {
  using namespace std;
  string s;
  va_list args;
  va_start(args, fmt);
  int chars_needed = vsnprintf(0, 0, fmt, args);
  va_end(args);
  va_start(args, fmt);
  try {
    vinsertf(s, s.end(), fmt, chars_needed, args);
  }
  catch (...) {
    va_end(args);
    throw;
  }
  va_end(args);
  return s;
}

// these have nearly identical implementations to stringf above:
std::string& appendf(std::string& s, char const* fmt, ...);
std::string& insertf(std::string& s, std::string::iterator it,
                    char const* fmt, ...);
Roger Pate
источник
@Neil: От man vsnprintf: «Эти функции возвращают количество напечатанных символов ... или значение отрицательного , если произошла ошибка вывода, за исключением того, для snprintf()и vsnprintf(), которые возвращают количество символов , которые бы были напечатаны , если п неисчерпаемы ... "Следовательно, фиктивный вызов с буфером 0 для измерения необходимого размера буфера.
Mike DeSimone
@Checkers: Ах, Boost. Такой большой потенциал, что мне тоже не дадут воспользоваться. Надеюсь, когда-нибудь. В любом случае, стал ли Boost настолько большим, что его невозможно полностью понять? Я был бы рад просто получить boost::spirit.
Майк Дезимоун
На самом деле это код Windows - из MSDN «Для _vsnprintf, если количество байтов для записи превышает буфер, то записывается счетчик байтов и возвращается –1». но я портирую его на Linux. Я не могу вспомнить, действительно ли мое приложение Linux использует это, или если я тестировал его для больших размеров буфера, должен это делать. Благодарю.
1
если вы все равно используете код Windows, продолжайте и используйте, _vscprintfчтобы определить необходимый размер буфера.
smerlin
6

is_sortedУтилита для тестирования контейнеров перед применением алгоритмов , как , includeкоторые ожидают отсортированный запись:

  template <
    class FwdIt
  >
  bool is_sorted(FwdIt iBegin, FwdIt iEnd)
  {
    typedef typename std::iterator_traits<FwdIt>::value_type value_type;
    return adjacent_find(iBegin, iEnd, std::greater<value_type>()) == iEnd;
  } // is_sorted

  template <
    class FwdIt,
    class Pred
  >
  bool is_sorted_if(FwdIt iBegin, FwdIt iEnd, Pred iPred)
  {
    if (iBegin == iEnd) return true;
    FwdIt aIt = iBegin;
    for (++aIt; aIt != iEnd; ++iBegin, ++aIt)
    {
      if (!iPred(*iBegin, *aIt)) return false;
    }
    return true;
  } // is_sorted_if

Да, я знаю, лучше было бы отрицать предикат и использовать предикатную версию adjacent_find:)

Матье М.
источник
1
при условии, что вы выполняете тест только в assert(): p
wilhelmtell
Вы не должны использовать венгерскую нотацию.
the_drow
3
@the_drow: большое спасибо за этот полезный комментарий :) Я не очень его фанат, но это требование там, где я работаю ... С тех пор я избавился от этой привычки, не волнуйтесь за мою душу;)
Матье M.
3

Определенно boost :: addressof

оборота острый зуб
источник
На самом деле, я слышал много дискуссий о перегрузке &, но я никогда, никогда не видел, чтобы кто-то действительно делал это (хм, может быть, bit_reference).
Виктор Сер
@Victor Sehr: ATL :: CComPtr ( msdn.microsoft.com/en-us/library/ezzw7k98(VS.80).aspx ) и _com_ptr_t ( msdn.microsoft.com/en-us/library/417w8b3b(VS.80 ) .aspx ) являются хорошими примерами.
Sharptooth
3
//! \brief Fills reverse_map from map, so that all keys of map 
//         become values of reverse_map and all values become keys. 
//! \note  This presumes that there is a one-to-one mapping in map!
template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 >
inline void build_reverse_map( const std::map<T1,T2,TP1,TA1>& map
                             ,       std::map<T2,T1,TP2,TA2>& reverse_map)
{
    typedef std::map<T1,T2,TP1,TA1>         map_type;
    typedef std::map<T2,T1,TP2,TA2>         r_map_type;
    typedef typename r_map_type::value_type r_value_type;

    for( typename map_type::const_iterator it=map.begin(),
                                          end=map.end(); it!=end; ++it ) {
        const r_value_type v(it->second,it->first);
        const bool was_new = reverse_map.insert(v).second;
        assert(was_new);
    }
}
оборота SBI
источник
Я предпочитаю использовать Boost.Bimapбиблиотеку (или Boost.MultiIndexдля более сложных ситуаций)
Matthieu M.
1
Я не понимаю, почему assert ()?
Виктор Сер
@Viktor: чтобы не было дублирующихся ключей reverse_map. Считайте maphas (1 -> «one»; 2 -> «one») reverse_mapполучит один элемент («one» -> 1). Утверждение поймает это. См. Также: bijection
bijection sbk
3
Кстати, sbi, наличие кода с побочными эффектами в assert () сильно укусит вас, если вы скомпилируете с NDEBUG и assert () будут полностью удалены.
сбк
2
ага, после обновления мой первый комментарий выглядит действительно глупо, stackoverflow - номер 1 при поиске моего имени в гугле, так что я надеюсь, что ни один будущий работодатель этого не увидит =)
Виктор Сер
3

Глядя на мои stl_util.h, многие из классических функций (функции удаления copy_if), а также на этот (вероятно, тоже довольно распространенный, но пока я не вижу его в ответах) для поиска по карте и возврата либо найденного значения, либо по умолчанию, аля getв Python dict:

template<typename K, typename V>
inline V search_map(const std::map<K, V>& mapping,
                    const K& key,
                    const V& null_result = V())
   {
   typename std::map<K, V>::const_iterator i = mapping.find(key);
   if(i == mapping.end())
      return null_result;
   return i->second;
   }

Использование по null_resultумолчанию сконструированного по умолчанию Vаналогично поведениюstd::map 's operator[], но это полезно, когда карта является константой (обычная для меня), или если построенный по умолчанию V не подходит для использования.

Джек Ллойд
источник
Что, если V - это int, float или какой-то другой примитив?
Inverse
Инициализация пустого значения работает для базовых типов в C ++. Для целых чисел и чисел с плавающей запятой это сделает значение по умолчанию null_result 0.
Джек Ллойд,
3

Вот мой набор дополнительных утилит, созданный на основе обертки std-algo boost.range, которая может вам понадобиться для некоторых функций. (это тривиально написать, это интересный материал)

#pragma once


/** @file
    @brief Defines various utility classes/functions for handling ranges/function objects
           in addition to bsRange (which is a ranged version of the \<algorithm\> header)

    Items here uses a STL/boost-style naming due to their 'templatised' nature.

    If template variable is R, anything matching range_concept can be used. 
    If template variable is C, it must be a container object (supporting C::erase())
*/

#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/smart_ptr.hpp>

namespace boost
{
struct use_default; 

template<class T>
class iterator_range;

#pragma warning(disable: 4348) // redeclaration of template default parameters (this clashes with fwd-decl in boost/transform_iterator.hpp)
template <
    class UnaryFunction
  , class Iterator
  , class Reference = use_default
  , class Value = use_default
>
class transform_iterator;

template <
    class Iterator
  , class Value = use_default
  , class Category   = use_default
  , class Reference  = use_default
  , class difference = use_default
>
class indirect_iterator;

template<class T>
struct range_iterator;

template <
    class Incrementable
  , class CategoryOrTraversal = use_default
  , class difference = use_default
>
class counting_iterator;

template <class Predicate, class Iterator>
class filter_iterator;

}

namespace orz
{

/// determines if any value that compares equal exists in container
template<class R, class T>
inline bool contains(const R& r, const T& v) 
{
    return std::find(boost::begin(r), boost::end(r), v) != boost::end(r);
}

/// determines if predicate evaluates to true for any value in container
template<class R, class F>
inline bool contains_if(const R& r, const F& f) 
{
    return std::find_if(boost::begin(r), boost::end(r), f) != boost::end(r);
}

/// insert elements in range r at end of container c
template<class R, class C>
inline void insert(C& c, const R& r)
{
    c.insert(c.end(), boost::begin(r), boost::end(r));
}
/// copy elements that match predicate
template<class I, class O, class P>
inline void copy_if(I i, I end, O& o, const P& p)
{
    for (; i != end; ++i) {
        if (p(*i)) {
            *o = *i;
            ++o;
        }
    }
}

/// copy elements that match predicate
template<class R, class O, class P>
inline void copy_if(R& r, O& o, const P& p)
{
    copy_if(boost::begin(r), boost::end(r), o, p);
}

/// erases first element that compare equal
template<class C, class T>
inline bool erase_first(C& c, const T& v) 
{
    typename C::iterator end = boost::end(c);
    typename C::iterator i = std::find(boost::begin(c), end, v);
    return i != c.end() ? c.erase(i), true : false;
}

/// erases first elements that match predicate
template<class C, class F>
inline bool erase_first_if(C& c, const F& f) 
{
    typename C::iterator end = boost::end(c);
    typename C::iterator i = std::find_if(boost::begin(c), end, f);
    return i != end ? c.erase(i), true : false;
}

/// erase all elements (doesn't deallocate memory for std::vector)
template<class C>
inline void erase_all(C& c) 
{
    c.erase(c.begin(), c.end());
}

/// erase all elements that compare equal
template<typename C, typename T>
int erase(C& c, const T& value)
{
    int n = 0;

    for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) {
        if (*i == value) {
            i = c.erase(i);
            ++n;
        } else {
            ++i;
        }
    }

    return n;
}

/// erase all elements that match predicate
template<typename C, typename F>
int erase_if(C& c, const F& f)
{
    int n = 0;

    for (boost::range_iterator<C>::type i = boost::begin(c); i != boost::end(c);) {
        if (f(*i)) {
            i = c.erase(i);
            ++n;
        } else {
            ++i;
        }
    }

    return n;
}


/// erases all consecutive duplicates from container (sort container first to get all)
template<class C>
inline int erase_duplicates(C& c)
{
    boost::range_iterator<C>::type i = std::unique(c.begin(), c.end());
    typename C::size_type n = std::distance(i, c.end());
    c.erase(i, c.end());
    return n;
}

/// erases all consecutive duplicates, according to predicate, from container (sort container first to get all)
template<class C, class F>
inline int erase_duplicates_if(C& c, const F& f)
{
    boost::range_iterator<C>::type i = std::unique(c.begin(), c.end(), f);
    typename C::size_type n = std::distance(i, c.end());
    c.erase(i, c.end());
    return n;
}

/// fill but for the second value in each pair in range
template<typename R, typename V>
inline void fill_second(R& r, const V& v)
{
    boost::range_iterator<R>::type i(boost::begin(r)), end(boost::end(r));

    for (; i != end; ++i) {
        i->second = v;
    }
}

/// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications
template<typename R1, typename R2, typename F>
void for_each2(R1& r1, R2& r2, const F& f)
{
    boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1));
    boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2));

    for(;i != i_end && j != j_end; ++i, ++j) {
        f(*i, *j);
    }    
}

/// applying function to corresponding pair through both ranges, min(r1.size(), r2,size()) applications
template<typename R1, typename R2, typename R3, typename F>
void for_each3(R1& r1, R2& r2, R3& r3, const F& f)
{
    boost::range_iterator<R1>::type i(boost::begin(r1)), i_end(boost::end(r1));
    boost::range_iterator<R2>::type j(boost::begin(r2)), j_end(boost::end(r2));
    boost::range_iterator<R3>::type k(boost::begin(r3)), k_end(boost::end(r3));

    for(;i != i_end && j != j_end && k != k_end; ++i, ++j, ++k) {
        f(*i, *j, *k);
    }    
}


/// applying function to each possible permutation of objects, r1.size() * r2.size() applications
template<class R1, class R2, class F>
void for_each_permutation(R1 & r1, R2& r2, const F& f)
{
    typedef boost::range_iterator<R1>::type R1_iterator;
    typedef boost::range_iterator<R2>::type R2_iterator;

    R1_iterator end_1 = boost::end(r1);
    R2_iterator begin_2 = boost::begin(r2);
    R2_iterator end_2 = boost::end(r2);

    for(R1_iterator i = boost::begin(r1); i != end_1; ++i) {
        for(R2_iterator j = begin_2; j != end_2; ++j) {
            f(*i, *j);
        }
    }
}

template <class R>
inline boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > > 
make_indirect_range(R& r)
{
    return boost::iterator_range<boost::indirect_iterator<typename boost::range_iterator<R>::type > > (r);
}

template <class R, class F>
inline boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> > 
make_transform_range(R& r, const F& f)
{
    return boost::iterator_range<boost::transform_iterator<F, typename boost::range_iterator<R>::type> >(
        boost::make_transform_iterator(boost::begin(r), f), 
        boost::make_transform_iterator(boost::end(r), f));
}

template <class T>
inline boost::iterator_range<boost::counting_iterator<T>  >
make_counting_range(T begin, T end)
{
    return boost::iterator_range<boost::counting_iterator<T> >(
        boost::counting_iterator<T>(begin), boost::counting_iterator<T>(end));
}

template <class R, class F>
inline boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> >
make_filter_range(R& r, const F& f)
{
    return boost::iterator_range<boost::filter_iterator<F, typename boost::range_iterator<R>::type> >(
        boost::make_filter_iterator(f, boost::begin(r), boost::end(r)),
        boost::make_filter_iterator(f, boost::end(r), boost::end(r)));
}

namespace detail {

template<class T>
T* get_pointer(T& p) {
    return &p;
}

}

/// compare member function/variable equal to value. Create using @ref mem_eq() to avoid specfying types 
template<class P, class V>
struct mem_eq_type
{
    mem_eq_type(const P& p, const V& v) : m_p(p), m_v(v) { }

    template<class T>
    bool operator()(const T& a) const {
        using boost::get_pointer;
        using orz::detail::get_pointer;
        return (get_pointer(a)->*m_p) == m_v;
    }

    P m_p;
    V m_v;
};


template<class P, class V>
mem_eq_type<P,V> mem_eq(const P& p, const V& v) 
{
    return mem_eq_type<P,V>(p, v);
}

/// helper macro to define function objects that compare member variables of a class
#define ORZ_COMPARE_MEMBER(NAME, OP) \
    template <class P> \
    struct NAME##_type \
    { \
        NAME##_type(const P&p) : m_p(p) {} \
        template<class T> \
        bool operator()(const T& a, const T& b) const { \
            return (a.*m_p) OP (b.*m_p); \
        } \
        P m_p; \
    }; \
    template <class P> \
    NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); }

#define ORZ_COMPARE_MEMBER_FN(NAME, OP) \
    template <class P> \
    struct NAME##_type \
    { \
        NAME##_type(const P&p) : m_p(p) {} \
        template<class T> \
        bool operator()(const T& a, const T& b) const { \
        return (a.*m_p)() OP (b.*m_p)(); \
    } \
        P m_p; \
    }; \
    template <class P> \
    NAME##_type<P> NAME(const P& p) { return NAME##_type<P>(p); }

/// helper macro to wrap range functions as function objects (value return)
#define ORZ_RANGE_WRAP_VALUE_2(FUNC, RESULT)                              \
    struct FUNC##_                                                \
    {                                                             \
        typedef RESULT result_type;                               \
        template<typename R, typename F>                          \
        inline RESULT operator() (R&  r, const F&  f) const       \
        {                                                         \
            return FUNC(r, f);                                    \
        }                                                         \
    };

/// helper macro to wrap range functions as function objects (void return)
#define ORZ_RANGE_WRAP_VOID_2(FUNC)                                 \
    struct FUNC##_                                                \
    {                                                             \
        typedef void result_type;                                 \
        template<typename R, typename F>                          \
        inline void operator() (R&  r, const F&  f) const         \
        {                                                         \
            FUNC(r, f);                                           \
        }                                                         \
    };

/// helper macro to wrap range functions as function objects (void return, one argument)
#define ORZ_RANGE_WRAP_VOID_1(FUNC)                                 \
    struct FUNC##_                                                \
    {                                                             \
        typedef void result_type;                                 \
        template<typename R>                          \
        inline void operator() (R&  r) const         \
        {                                                         \
            FUNC(r);                                           \
        }                                                         \
    }; 

ORZ_RANGE_WRAP_VOID_2(for_each);
ORZ_RANGE_WRAP_VOID_1(erase_all);
ORZ_RANGE_WRAP_VALUE_2(contains, bool);
ORZ_RANGE_WRAP_VALUE_2(contains_if, bool);
ORZ_COMPARE_MEMBER(mem_equal, ==)
ORZ_COMPARE_MEMBER(mem_not_equal, !=)
ORZ_COMPARE_MEMBER(mem_less, <)
ORZ_COMPARE_MEMBER(mem_greater, >)
ORZ_COMPARE_MEMBER(mem_lessequal, <=)
ORZ_COMPARE_MEMBER(mem_greaterequal, >=)
ORZ_COMPARE_MEMBER_FN(mem_equal_fn, ==)
ORZ_COMPARE_MEMBER_FN(mem_not_equal_fn, !=)
ORZ_COMPARE_MEMBER_FN(mem_less_fn, <)
ORZ_COMPARE_MEMBER_FN(mem_greater_fn, >)
ORZ_COMPARE_MEMBER_FN(mem_lessequal_fn, <=)
ORZ_COMPARE_MEMBER_FN(mem_greaterequal_fn, >=)

#undef ORZ_COMPARE_MEMBER
#undef ORZ_RANGE_WRAP_VALUE_2
#undef ORZ_RANGE_WRAP_VOID_1
#undef ORZ_RANGE_WRAP_VOID_2
}
Macke
источник
+1 для for_each_permutation (...) , в основном потому, что я написал аналогичную оболочку =). Но почему erase_duplicates (...) возвращает подписанный int?
Виктор Сер
Привет Виктор! Думаю, вы тоже должны были видеть for_each_permutation на своей предыдущей работе. ;) erase_duplicates возвращает количество стертых элементов, что полезно для ведения журнала и отладки.
Macke
Хм, возможно, просмотрел его, не понимая, что он делает ;) , в любом случае, я понимаю, почему он возвращает целое число, я просто не понимаю, почему целое число подписано (или, если быть более конкретным, почему оно не беззнаковое )?
Виктор Сер
Ах. Просто лень с моей стороны :-П. size_t - подходящий тип.
Macke
3

Мне кажется, мне нужно декартово произведение, например {A, B}, {1, 2} -> {(A, 1), (A, 2), (B, 1), (B, 2)}

// OutIt needs to be an iterator to a container of std::pair<Type1, Type2>
template <typename InIt1, typename InIt2, typename OutIt>
OutIt
cartesian_product(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt out)
{
    for (; first1 != last1; ++first1)
        for (InIt2 it = first2; it != last2; ++it)
            *out++ = std::make_pair(*first1, *it);
    return out;
}
rlbond
источник
Обратите внимание, что InIt2 должен быть прямым итератором вместо итератора ввода. Итераторы ввода не подходят для нескольких проходов.
2

Я бы назвал такую ​​функцию добавления по ее имени и использовал бы operator + =, operator * = и так далее для поэлементных операций, таких как:

    template<typename X> inline void operator+= (std::vector<X>& vec1, const X& value)
    {
      std::transform( vec1.begin(), vec1.end(), vec1.begin(), std::bind2nd(std::plus<X>(),value) );
    }

    template<typename X> inline void operator+= (std::vector<X>& vec1, const std::vector<X>& vec2)
    {
      std::transform( vec1.begin(), vec1.end(), vec2.begin(), vec1.begin(), std::plus<X>() );
    }

некоторые другие простые и очевидные обертки, как предполагалось ранее:

    template<typename X> inline void sort_and_unique(std::vector<X> &vec)
    {
        std::sort( vec.begin(), vec.end() );
        vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
    }


    template<typename X> inline void clear_vec(std::vector<X> &vec)
    {
        std::vector<X>().swap(vec);
    }


    template<typename X> inline void trim_vec(std::vector<X> &vec, std::size_t new_size)
    {
        if (new_size<vec.size())
            std::vector<X>(vec.begin(),vec.begin() + new_size).swap(vec);
        else
            std::vector<X>(vec).swap(vec);
    }
Датчанин
источник
7
Эти операторы - очень хороший пример того, почему перегрузка операторов должна выполняться редко. Я бы подумал, vec+=valчто добавляет значение к вектору. (См. Stackoverflow.com/questions/2551775/. ) Теперь, когда я увидел вашу реализацию, я думаю, что это столь же правильная интерпретация значения +=. Я бы не знал, какой из них был бы правильным или неправильным, так что, вероятно, хорошо, что у нас нет +=for std::vector.
sbi 02
1
@sbi Согласен. Я считаю, что мне не operator+()хватает удивительного раннего понимания стандарта. Обычно я ожидаю операции O (1) везде, где вижу оператор плюса. C ++ делает дорогостоящие или опасные вещи более подробными или сложными, и мне это нравится. Взгляните на Java: одна из худших ошибок программирования - это злоупотребление оператором «плюс». Конечно, опять же, C ++ не всегда упрощает дешевые и быстрые вещи, но эй. Хорошие программисты на C ++ очень осведомлены о производительности. ;)
Wilhelmtell
2
Я согласен с вами обоими, что op+()вообще не следует давать определения из-за его двусмысленности. Но векторы обычно являются частью (математического) векторного пространства, и существует каноническое определение сложения двух векторов и скалярного умножения. Чтобы продолжить ваш аргумент: простой double- это также вектор, поэтому, если вы добавите две doubleпеременные, как a+bтогда, вы ожидаете получить новую, doubleа не pairдвойную подобную (a,b). Умножение на скаляр тоже канонично, но умножение двух векторов - нет. Так что с перегрузкой нужно делать осторожно ..
датчанин
1

Вставьте новый элемент и верните его, что полезно для простой семантики перемещения, например, push_back(c).swap(value)и связанных случаев.

template<class C>
typename C::value_type& push_front(C& container) {
  container.push_front(typename C::value_type());
  return container.front();
}

template<class C>
typename C::value_type& push_back(C& container) {
  container.push_back(typename C::value_type());
  return container.back();
}

template<class C>
typename C::value_type& push_top(C& container) {
  container.push(typename C::value_type());
  return container.top();
}

Вставить и вернуть товар:

template<class C>
typename C::value_type pop_front(C& container) {
  typename C::value_type copy (container.front());
  container.pop_front();
  return copy;
}

template<class C>
typename C::value_type pop_back(C& container) {
  typename C::value_type copy (container.back());
  container.pop_back();
  return copy;
}

template<class C>
typename C::value_type pop_top(C& container) {
  typename C::value_type copy (container.top());
  container.pop();
  return copy;
}
Роджер Пэйт
источник
1

IMO должно быть больше функциональности для pair:

#ifndef pair_iterator_h_
#define pair_iterator_h_

#include <boost/iterator/transform_iterator.hpp>    
#include <functional>
#include <utility>    

// pair<T1, T2> -> T1
template <typename PairType>
struct PairGetFirst : public std::unary_function<PairType, typename PairType::first_type>
{
    typename typename PairType::first_type& operator()(PairType& arg) const
    {       return arg.first;   }
    const typename PairType::first_type& operator()(const PairType& arg) const
    {       return arg.first;   }
};



// pair<T1, T2> -> T2
template <typename PairType>
struct PairGetSecond : public std::unary_function<PairType, typename PairType::second_type>
{
    typename PairType::second_type& operator()(PairType& arg) const
    {       return arg.second;  }
    const typename PairType::second_type& operator()(const PairType& arg) const
    {       return arg.second;  }
};



// iterator over pair<T1, T2> -> iterator over T1
template <typename Iter>
boost::transform_iterator<PairGetFirst<typename std::iterator_traits<Iter>::value_type>, Iter> 
make_first_iterator(Iter i)
{
    return boost::make_transform_iterator(i, 
        PairGetFirst<typename std::iterator_traits<Iter>::value_type>());
}



// iterator over pair<T1, T2> -> iterator over T2
template <typename Iter>
boost::transform_iterator<PairGetSecond<typename std::iterator_traits<Iter>::value_type>, Iter> 
make_second_iterator(Iter i)
{
    return boost::make_transform_iterator(i, 
        PairGetSecond<typename std::iterator_traits<Iter>::value_type>());
}



// T1 -> pair<T1, T2>
template <typename FirstType, typename SecondType>
class InsertIntoPair1st : public std::unary_function<FirstType, std::pair<FirstType, SecondType> >
{
public:
    InsertIntoPair1st(const SecondType& second_element) : second_(second_element) {}
    result_type operator()(const FirstType& first_element)
    {
        return result_type(first_element, second_);
    }
private:
    SecondType second_;
};



// T2 -> pair<T1, T2>
template <typename FirstType, typename SecondType>
class InsertIntoPair2nd : public std::unary_function<SecondType, std::pair<FirstType, SecondType> >
{
public:
    InsertIntoPair2nd(const FirstType& first_element) : first_(first_element) {}
    result_type operator()(const SecondType& second_element)
    {
        return result_type(first_, second_element);
    }
private:
    FirstType first_;
};

#endif // pair_iterator_h_
rlbond
источник
1
Почему бы не переместить PairTypeшаблон в operator ()? Кроме того, зарезервированы двойные подчеркивания в идентификаторе.
GManNickG
@GMan - Потому что тогда вы не можете использовать unary_function, что мне нужно в какой-то момент в моем коде. Что касается двойного подчеркивания, спасибо, что сообщили мне - мне нужно это изменить.
rlbond
При этом используются зависимые имена (аргумент_тип, результат_тип) неправильно, и компиляторы должны отклонить его. "В определении шаблона класса или члена шаблона класса, если базовый класс шаблона класса зависит от параметра шаблона, область действия базового класса не проверяется во время поиска неквалифицированного имени либо в точке определения шаблон или член класса или во время создания экземпляра шаблона или члена класса ". [14.6.2 / 3, C ++ 03]
@ Роджер Пейт: Я не знал об этом правиле. Теперь это исправлено.
rlbond
1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

Если вам нужно использовать много функций, которые принимают указатель + количество байтов, всегда просто

fun(vec.data(), bytesize(vec));
оборота hrehfeld
источник
1

Дублируйте строку с *:

std::string operator*(std::string s, size_t n)
{
    std::stringstream ss;
    for (size_t i=0; i<n; i++) ss << s;
    return ss.str();
}
kirbyfan64sos
источник
0

Один из моих любимых - Transposerэто перестановка кортежа контейнеров одинакового размера. То есть, если у вас есть tuple<vector<int>,vector<float>>, он преобразует его в vector<tuple<int, float>>. Удобно при программировании XML. Вот как я это сделал.

#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include <algorithm>
#include <stdexcept>

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <boost/type_traits.hpp>

using namespace boost;

template <class TupleOfVectors>
struct GetTransposeTuple;

template <>
struct GetTransposeTuple<tuples::null_type>
{
  typedef tuples::null_type type;
};

template <class TupleOfVectors>
struct GetTransposeTuple
{
  typedef typename TupleOfVectors::head_type Head;
  typedef typename TupleOfVectors::tail_type Tail;
  typedef typename
    tuples::cons<typename remove_reference<Head>::type::value_type,
                 typename GetTransposeTuple<Tail>::type> type;
};

template <class TupleOfVectors,
          class ValueTypeTuple = 
                typename GetTransposeTuple<TupleOfVectors>::type,
          unsigned int TUPLE_INDEX = 0>
struct Transposer
  : Transposer <typename TupleOfVectors::tail_type,
                ValueTypeTuple,
                TUPLE_INDEX + 1>
{
  typedef typename remove_reference<typename TupleOfVectors::head_type>::type
    HeadContainer;
  typedef typename TupleOfVectors::tail_type Tail;
  typedef Transposer<Tail, ValueTypeTuple, TUPLE_INDEX + 1> super;
  typedef std::vector<ValueTypeTuple> Transpose;

  Transposer(TupleOfVectors const & tuple)
    : super(tuple.get_tail()),
      head_container_(tuple.get_head()),
      head_iter_(head_container_.begin())
  {}

  Transpose get_transpose ()
  {
    Transpose tran;
    tran.reserve(head_container_.size());
    for(typename HeadContainer::const_iterator iter = head_container_.begin();
        iter != head_container_.end();
        ++iter)
    {
      ValueTypeTuple vtuple;
      this->populate_tuple(vtuple);
      tran.push_back(vtuple);
    }
    return tran;
  }

private:

  HeadContainer const & head_container_;
  typename HeadContainer::const_iterator head_iter_;

protected:

  void populate_tuple(ValueTypeTuple & vtuple)
  {
    if(head_iter_ == head_container_.end())
      throw std::runtime_error("Container bound exceeded.");
    else
    {
      vtuple.get<TUPLE_INDEX>() = *head_iter_++;
      super::populate_tuple (vtuple);
    }
  }
};

template <class ValueTypeTuple,
          unsigned int INDEX>
struct Transposer <tuples::null_type, ValueTypeTuple, INDEX>
{
  void populate_tuple(ValueTypeTuple &) {}
  Transposer (tuples::null_type const &) {}
};

template <class TupleOfVectors>
typename Transposer<TupleOfVectors>::Transpose
transpose (TupleOfVectors const & tupleofv)
{
  return Transposer<TupleOfVectors>(tupleofv).get_transpose();
}

int main (void)
{
  typedef std::vector<int> Vint;
  typedef std::list<float> Lfloat;
  typedef std::vector<long> Vlong;

  Vint vint;
  Lfloat lfloat;
  Vlong vlong;

  std::generate_n(std::back_inserter(vint), 10, rand);
  std::generate_n(std::back_inserter(lfloat), 10, rand);
  std::generate_n(std::back_inserter(vlong), 10, rand);

  typedef tuples::tuple<Vint, Lfloat, Vlong> TupleOfV;
  typedef GetTransposeTuple<TupleOfV>::type TransposeTuple;

  Transposer<TupleOfV>::Transpose tran = 
    transpose(make_tuple(vint, lfloat, vlong));
  // Or alternatively to avoid copying
  // transpose(make_tuple(ref(vint), ref(lfloat), ref(vlong)));
  std::copy(tran.begin(), tran.end(),
            std::ostream_iterator<TransposeTuple>(std::cout, "\n"));

  return 0;
}
Сумант
источник
0

Не уверен, что они подходят как обертки std, но мои часто используемые вспомогательные функции:

void split(string s, vector<string> parts, string delims);
string join(vector<string>& parts, string delim);
int find(T& array, const V& value);
void assert(bool condition, string message);
V clamp(V value, V minvalue, V maxvalue);
string replace(string s, string from, string to);
const char* stristr(const char* a,const char*b);
string trim(string str);
T::value_type& dyn(T& array,int index);

T и V здесь аргументы шаблона. Последняя функция работает так же, как [] -оператор, но с автоматическим изменением размера под нужный индекс.

АареП
источник
1
Имя assert зарезервировано (во всех областях) стандартной библиотекой для макроса с таким именем.
1
Я думаю, что есть также макрос assert (), объявленный в заголовках windows или mfc. Оба они терпят неудачу в событии WM_PAINT, потому что отображение диалогового окна подтверждения в некоторых случаях запускает следующую оценку. Так что, в конце концов, нет ничего страшного в замене этих глючных реализаций на третью. Все, что вам нужно сделать, это явно повторно объявить собственный макрос assert после #include <assert> или просто использовать #undef assert.
AareP
0

Подобно тому, что люди писали ранее, у меня есть удобные перегрузки алгоритмов для упрощения передачи аргументов итератора. Я называю такие алгоритмы:

for_each(iseq(vec), do_it());

Я перегрузил все алгоритмы так, что они принимают один параметр типа input_sequence_range<>вместо двух итераторов ввода (ввод как и во всем, что не является простым выводом).

template<typename In>
struct input_sequence_range
: public std::pair<In,In>
{
    input_sequence_range(In first, In last)
        : std::pair<In,In>(first, last)
    { }
};

И вот как iseq()работает:

template<typename C>
input_sequence_range<typename C::const_iterator> iseq(const C& c)
{
    return input_sequence_range<typename C::const_iterator>(c.begin(),
                                                            c.end());
}

Точно так же у меня есть специализация на

  • const_iterators
  • указатели (примитивные массивы)
  • итераторы потока
  • любой диапазон [начало, конец) только для единообразного использования: используйте iseq () для всего
Wilhelmtell
источник
3
... или вы можете просто использовать Boost.Range и получить преимущества адаптеров диапазонов и проверенного экспертами, широко протестированного кода.
Mankarse 03
0

Неупорядоченное стирание для std::vector. Самый эффективный способ стереть элемент из a, vectorно он не сохраняет порядок элементов. Я не видел смысла распространять его на другие контейнеры, так как большинство из них не имеют такого же наказания за удаление элементов из середины. Он похож на некоторые другие уже опубликованные шаблоны, но он используется std::swapдля перемещения элементов вместо копирования.

template<typename T>
void unordered_erase(std::vector<T>& vec, const typename std::vector<T>::iterator& it)
{
    if (it != vec.end()) // if vec is empty, begin() == end()
    {
        std::swap(vec.back(), *it);
        vec.pop_back();
    }
}

Сигнум возвращает признак типа. Возвращает -1отрицательные, 0нулевые и 1положительные значения.

template <typename T>
int signum(T val)
{
    return (val > T(0)) - (val < T(0));
}

Clamp довольно понятен, он фиксирует значение так, чтобы оно лежало в заданном диапазоне. Она поражает мой взгляд , что стандартная библиотека включает в себя minи , maxно неclamp

template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}
Фибблы
источник