Які функції обгортки стандартної бібліотеки 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
@Billy насправді, CW насправді не є виправданням для задавання суб'єктивних питань. Я зміню заголовок, що повинно зробити людей щасливими.

4
@kts: Оскільки vector :: insert отримує ітератори довільного доступу, хороша реалізація використовуватиме диспетчеризацію часу компіляції, щоб зробити це самостійно.

4
Чи не краще написати, c.swap(C())щоб очистити контейнер?
Олександр К.

5
@Alexandre: Це заборонено: воно прив'язує тимчасове до неконстантного посилання. C (). Swap (c) буде працювати.

Відповіді:


37

boost :: array

містить (контейнер, 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 може бути швидшим).


4
Хтось ще вважає, що містить третій шаблон ( bool sorted=false) та спеціалізацію, коли sorted==trueдзвонити binary_searchзамість цього find?
KitsuneYMG

6
@kts: Коли ви знаєте, що контейнер відсортований, просто зателефонуйте binary_search безпосередньо.

2
boost :: array Еквівалент STL доступний у просторі імен tr1 на найновіших компіляторах (навіть codewarrior): std :: tr1 :: array <>
Klaim

36

Досить часто я використовував вектор як набір елементів у певному порядку (і, очевидно, коли мені не потрібні швидкі перевірки is-this-element-in-the-set). У цих випадках виклик erase () є марною тратою часу, оскільки він змінить порядок елементів, і я не дбаю про порядок. Ось тоді функція O (1) нижче стане в нагоді - просто перемістіть останній елемент у позицію того, який ви хочете видалити:

template<typename T>
void erase_unordered(std::vector<T>& v, size_t index)
{
    v[index] = v.back();
    v.pop_back();
}

Хороший. Не думав робити це так .. :) Має бути обгортка "Сумка" (схожа на стек та чергу), яка пришвидшує ці операції, коли порядок не важливий.
Макке

12
А в C ++ 0x v[index] = st::move(v.back()); v.pop_back();- приблизно настільки ж ефективний, наскільки він отримує.
GManNickG

@Matthieu: Подивіться на позначку часу на них. : П.І. зауважив через пару годин, далеко поза дозволеним часом редагування.
GManNickG

@GMan: ти впевнений у цьому? мені здається, що v.pop_back () може призвести до невизначеної поведінки, оскільки буде викликаний деструктор.
Viktor Sehr,

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);
}

1
@Billy: Це для автоматичного відновлення значення пізніше. (І вал слід вилучити з ктора.)

Вибачте, я все ще загубився (я не знайомий із 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;
}

2
Не відповідає на запитання, не обгортка для stdlib.

10
@Roger Pate, так, я знаю, тому відповідь починається словами "Насправді не обгортка, але ....".
Глен,

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;}

2
У мене теж є. :) +1 Для того, що це варте, вам потрібно лише два (відкиньте версії const). Коли масив буде const, Tбуде, const Uі ви отримаєте призначену функцію.
GManNickG

@GMan: Була якась версія GCC, яка не компілювала б якийсь код лише з неверсіями const, тому і constіснують версії. Оскільки це могло бути помилкою саме цієї версії GCC, я їх видалю.
sbi

1
@Marcus: Вони набагато старші за Boost.Range. :)
sbi

4
@Roger: Він обгортає масиви для їх використання зі стандартною бібліотекою. Ось так. :)
sbi

2
@Stacked, @sbe: Масиви ніколи не передаються за значенням, незалежно від того, чи є файл &. &Є для того, щоб тип відрахування довжини масиву.
Манкарсе

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();
}

Буде оновлювати ще, як я їх пам’ятаю. : P


3
Хе-хе - свого роду ярлик для boost::lexical_cast<t, t>.
Billy ONeal

1
так, це чудово, якщо ви не хочете підштовхувати проект
Стів

11
@BillyONeal: Ось чому я більше не використовую його. @ Steve: Ось чому я все ще ним користуюся.
Джон Перді

Було б надмірно дорого зателефонувати за номером a char*або a std::string. Можливо, шаблонна спеціалізація в порядку?
Вільгельмтель

Якщо я добре пам’ятаю, маю boost::lexical_castкупу таких спеціалізацій та перевірок помилок. Однак, щоб урізнити непарне число, це добре працює.
Джон Перді,

9

Функція утиліти у всілякому наборі інструментів, звичайно, є copy_if. Не дуже обгортка хоч.

Ще одним помічником, яким я зазвичай користуюсь deleter, є функтор, з яким я використовую std::for_eachдля видалення всіх покажчиків у контейнері.

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


+1 для функцій видалення. Мій функтор видалення добре працює з більшістю контейнерів, однак я бавився з тим, щоб він працював зі std :: map, де або ключ, або значення є покажчиком. Я спробував використати риси типу для вирішення проблеми, але насправді не надто далеко зайшов через обмеження в часі. Ви вирішили цю проблему чи вважаєте проблемою?
Глен

2
@Matthieu M., на жаль, не всі з нас можуть використовувати підсилення.
Глен

@Glen: Усі ці проблеми можуть бути вирішені за допомогою розумних покажчиків, бажано (але не обов'язково) з посилення. Важливим побічним ефектом є те, що контейнери з вказівниками на динамічно розподілені об'єкти раптом також стають винятковими.
sbi

@Glen тут, тут для проектів, які не передбачають інших бібліотек, крім STD, до Boost або TR1 включно.
Wheaties

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

звичайно, вам слід перевірити size()результат, перш ніж отримувати свої рядки.
Еван Теран,

І звідки я знаю, скільки рядків повинен мати результат?
sbi

2
@sbi: Ваш коментар підняв мій інтерес до того, що насправді може піти не так (крім рядка, просто не маючи достатньої кількості токенів для отримання) з stringstream/ getlineциклом. Я поставив запитання щодо цього тут: stackoverflow.com/questions/2562906/…
Еван Теран,

2
@ Еван: Я виправлений. Дивіться мій коментар на stackoverflow.com/2563542#2563542 . Вибачте.
sbi

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.
Billy ONeal

Ну, у мене є для цього адаптація контейнерів більшості алгоритмів STL :) Але саме цей я використовую найбільше, зазвичай, якщо я хочу унікальність, setдля початку використовую a .
Matthieu M.

@Matthieu M .: Тільки майте на увазі, роблячи це, у вас будуть люди, які постійно працюють зі STL, і в їх головах спрацьовують дзвінки тривоги "ПОПЕРЕДЖЕННЯ: ВИДАЛІТЬ АЛГОРИТМ, ЩО ВИДАЛИТИ БЕЗ ЗАКЛИКАННЯ ВИДАЛИТИ !!" . Насправді в цьому немає нічого поганого, але я б не робив цього, якби мені потрібно було поділитися своїм кодом серед багатьох програмістів. Тільки мої 2 центи.
Billy ONeal

1
@Billy: саме тому я назвав його стерти, а не видалити. Я не можу багато чого зробити, крім того, крім того, щоб дозволити їм ознайомитися з кодом. На щастя, у сучасній IDE до цього визначення лише один клік :)
Matthieu M.

7

Обгортковий спринт

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

Мета полягає в роз'єднанні форматування з виводу, не потрапляючи в проблеми зі спринтом та подібними. Це не красиво, але дуже корисно, особливо якщо ваші інструкції з кодування забороняють потоки iostream.


Ось версія, яка виділяє за потреби Ніла Баттерворта. [Переглянути історію версій версії Майка, яку я видалив як підмножину двох інших. Він схожий на Ніла, за винятком того, що останній є винятково безпечним, використовуючи вектор замість видалення []: 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] );
}

Ось версія, яка визначає необхідний розмір спереду, від Роджера Пейта . Для цього потрібні wditable 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, ...);

@Neil: Від man vsnprintf: «Ці функції повертають кількість надрукованих символів ... або значення негативного , якщо сталася помилка виведення, за винятком того, для snprintf()і vsnprintf(), які повертають кількість символів , які б були надруковані , якщо п невичерпні ... "Отже, фіктивний виклик з буфером 0 для вимірювання необхідного розміру буфера.
Mike DeSimone

@Checkers: Ах, підсилюй. Настільки великий потенціал, що мені теж не дозволяють використовувати. Одного разу, сподіваюся. У будь-якому випадку, Boost став настільки великим, що його ще неможливо зрозуміти до кінця? Я був би радий просто отримати boost::spirit.
Mike DeSimone

Це насправді код Windows - з MSDN "Для _vsnprintf, якщо кількість байтів для запису перевищує буфер, тоді записується кількість байтів і повертається –1". але я переношу це на Linux. Я не пам’ятаю, чи справді це використовує моя програма Linux, чи тестував я її там на великі розміри буфера - повинен це зробити. Дякую.

1
якщо ви все-таки використовуєте код Windows, продовжуйте і використовуйте _vscprintfдля визначення необхідного розміру буфера.
смерлін

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: велике спасибі за цей корисний коментар :) Я не настільки прихильний до цього, але це робота, яку я вимагаю ... З тих пір я позбувся звички, не хвилюйся за свою душу;)
Матьє М.

3

Безумовно, boost :: addressof


Насправді я чув багато дискусій про перевантаження &, але ніколи, ніколи не бачив, щоб хтось насправді це робив (можливо, хм bit_reference).
Віктор Шер


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);
    }
}

Я вважаю за краще користуватися Boost.Bimapбібліотекою (або Boost.MultiIndexдля більш складних ситуацій)
Матьє М.,

1
Я не розумію, чому assert ()?
Віктор Шер,

@Viktor: щоб переконатися у відсутності дубльованих ключів reverse_map. Розглянемо maphas (1 -> "one"; 2 -> "one") reverse_mapотримає один елемент ("one" -> 1). Твердження це зрозуміє. Дивіться також:
біекція

3
До речі, sbi, маючи код з побічними ефектами в assert (), він буде вас дуже погано кусати, як тільки ви скомпілюєте за допомогою NDEBUG, і assert () буде повністю позбавлений.
sbk

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[], але це корисно, коли карта const (загальна для мене), або якщо побудована за замовчуванням V не є правильною річчю для використання.


Що робити, якщо V - це int або float чи якийсь інший примітив?
Інверс

Порожня ініціалізація значень працює для основних типів у C ++. Для цілих чисел і плаваючих значень це буде за замовчуванням null_result 0.
Джек Ллойд

3

Ось мій набір додаткових утиліт, побудований поверх обгортки boost.range'ish std-algo, яка вам може знадобитися для деяких функцій. (це тривіально писати, це цікаві речі)

#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
}

+1 для for_each_permutation (...) , головним чином тому, що я написав подібну обгортку =). Але чому erase_duplicates (...) повертає підписаний int?
Viktor Sehr,

Привіт Вікторе! Ви, мабуть, бачили for_each_permutation і на попередній роботі. ;) erase_duplicates повертає кількість стираних елементів, що корисно для реєстрації та налагодження.
Macke

Хм, можливо, проглядав його, не розуміючи, що робить ;) , у будь-якому випадку, я розумію, чому він повертає ціле число, я просто не розумію, чому ціле число підписане (або якщо бути більш конкретним; чому воно не без підпису )?
Віктор Шер,

Ага. Просто лінь з мого боку :-P. size_t - відповідний тип.
Маке

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;
}

Зверніть увагу, що InIt2 повинен бути прямим ітератором замість вхідного ітератора. Ітератори введення не підходять для кількох проходів.

2

Я б назвав таку функцію додавання за її назвою і використовував би оператор + =, оператор * = тощо для елементарних операцій, таких як:

    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/. ) Тепер, коли я побачив вашу реалізацію, я думаю, що це настільки ж правильна інтерпретація значення +=теж. Я не знаю , що б бути правильним чи неправильним, так що це , ймовірно , так само добре , що у нас немає +=для std::vector.
sbi

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

ІМО має бути більше функціональних можливостей для 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_

1
Чому б не перемістити PairTypeшаблон до оператора ()? Також подвійне підкреслення в ідентифікаторі зарезервовано.
GManNickG,

@GMan - Тому що тоді ти не можеш використовувати unary_function, що мені потрібно в якийсь момент у моєму коді. Щодо подвійних підкреслень, дякую, що повідомили - мені потрібно буде це змінити.
rlbond

Це використовує залежні імена (аргумент_типу, результат_типу) неправильно, і компілятори повинні його відхилити. "У визначенні шаблону класу або члена шаблону класу, якщо базовий клас шаблону класу залежить від шаблону-параметра, область базового класу не перевіряється під час некваліфікованого пошуку імені або в точці визначення шаблон або член класу або під час створення шаблону класу або елемента. " [14.6.2 / 3, C ++ 03]

@Roger Pate: Я не знав цього правила. Зараз це виправлено.
rlbond

1
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

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

fun(vec.data(), bytesize(vec));

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();
}

0

Одним з моїх улюблених є те, Transposerщо знаходить транспонування корпу контейнерів однакового розміру. Тобто, якщо у вас є a tuple<vector<int>,vector<float>>, він перетворює його в a 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 - це аргументи шаблону. Остання функція працює так само, як і [] -operator, але з автоматизованою зміною розміру відповідно до необхідного індексу.


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 () для всього

3
... або ви можете просто використовувати Boost.Range і отримати переваги адаптерів діапазону та рецензованого, широко перевіреного коду.
Манкарсе

0

Невпорядковане стирання для std::vector. Найефективніший спосіб стирання елемента з, 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();
    }
}

Signum повертає знак типу. Повертає -1для від’ємних, 0нульових та 1додатних значень.

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

Затискач досить зрозумілий, він затискає значення так, щоб воно знаходилося в заданому діапазоні. Вона вражає мій погляд , що стандартна бібліотека включає в себе 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.