你使用哪种C++标准库包装器函数?


Which C++ Standard Library wrapper functions do you use?

这个问题,今天早上问,让我想知道你认为哪些特征是从C++标准库中丢失的,以及你是如何用包装器函数来填补空白的。例如,我自己的实用程序库具有矢量附加的功能:

1
2
3
4
5
6
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:

1
2
3
4
template <class C>
void Clear( C & c ) {
    c = C();
}

我还有一些,但我对你用的是哪一种感兴趣?请限制对包装函数的回答-即不超过几行代码。


Boo::数组

包含(容器,val)(非常简单,但很方便)。

1
2
3
4
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);
}

移除不稳定(开始、结束、值)

更快的std::remove版本,但不保留其余对象的顺序。

1
2
3
4
5
6
7
8
9
10
11
12
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可能更快)。


我经常使用vector作为一组没有特定顺序的项(很明显,当我不需要fast时,在集合检查中就是这个元素)。在这些情况下,调用erase()是浪费时间的,因为它将重新排序元素,而我不关心顺序。这时,下面的o(1)函数就派上用场了——只需将最后一个元素移动到要删除的元素的位置:

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


1
2
3
4
5
6
7
8
9
10
11
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对象超出范围时,它都将被重置。这样使用:

1
2
3
4
5
6
7
8
9
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
}

下面是另一种使用范围保护技巧的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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);
}

这只是一点锅炉板代码,但允许更清洁的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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);
}

不过,它取消了在类中使用的能力。

下面是实现相同效果的第三种方法(不存在可能抛出析构函数的问题):

实施:

1
//none -- it is built into the language

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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);
}


不是包装纸,而是臭名昭著的丢失的copy_if。从这里

1
2
3
4
5
6
7
8
9
10
11
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;
}


1
2
3
4
5
template< typename T, std::size_t sz >
inline T* begin(T (&array)[sz]) {return array;}

template< typename T, std::size_t sz >
inline T* end  (T (&array)[sz]) {return array + sz;}


有时候我觉得自己像在江户十一〔12〕和江户十三〔13〕地狱里。我想要一些功能,比如:

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

以及其他类似的std::findstd::for_each,以及基本上所有的STL算法。

我觉得sort(x)sort(x.begin(), x.end())快得多。


Everyones工具箱中的实用程序功能当然是copy_if。不过不是包装纸。

我通常使用的另一个助手是deleter,它是我与std::for_each一起使用的一个函数,用于删除容器中的所有指针。

[编辑]在我的"某物"中,我还发现了vector StringSplit(wstring const&, wchar_t);


我有一个头,它将以下内容放在"util"名称空间中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 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;
}


我不再使用这个了,但它曾经是一种主食:

1
2
3
4
5
6
template<typename T>
std::string make_string(const T& data) {
    std::ostringstream stream;
    stream << data;
    return stream.str();
}

会更新更多我记得他们。P


极其缺失的erase算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  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
2
3
4
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及其ilk的情况下将格式与输出分离。它不是很漂亮,但是非常有用,特别是如果您的编码指南禁止iostreams。

这是Neil Butterworth根据需要分配的版本。[查看Mike版本的修订历史记录,我将其作为其余两个版本的子集删除。它类似于Neil的方法,但后者使用vector而不是delete是异常安全的:字符串的ctor将在分配失败时引发。Mike的也使用了稍后显示的相同技术来确定前面的尺寸。RP]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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] );
}

这是一个版本,它决定了前面所需的大小,从罗杰·帕特。这需要可写的STD::Stand,它是由流行的实现提供的,但是是由C++ 0x(马库斯版本的视图修订历史)明确要求的,因为它略有不同,但基本上是下面的子集。RP]

实施

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
}

公共接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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, ...);


is_sorted实用程序,用于在应用诸如include之类的算法之前测试容器,这些算法需要经过排序的条目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  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的谓词版本。


肯定是Boost::AddressOf


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//! \brief Fills reverse_map from map, so that all keys of map
//         become values of reverse_map and all values become keys.
//!
ote  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);
    }
}


看看我的stl_util.h,许多经典(deleter函数,copy_if)和这一个(可能也很常见,但我还没有看到它在响应中给出),搜索地图并返回找到的值或默认值,在python的dict中的ala get

1
2
3
4
5
6
7
8
9
10
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;
   }

使用默认构造的V的默认null_resultstd::mapoperator[]的行为大致相同,但当映射为const(对我来说是公共的)或如果默认构造的v不是正确的使用对象时,这很有用。


这是我的一组额外的实用程序,建立在一个boost.range的std algo包装之上,您可能需要一些函数。(写起来很琐碎,这是有趣的东西)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#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 \ 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
}


我似乎需要一个笛卡尔积,例如a,b,1,2->(a,1),(a,2),(b,1),(b,2)

1
2
3
4
5
6
7
8
9
10
// 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;
}


我将按其名称调用这样的append函数,并将使用operator+=、operator*=等进行元素操作,例如:

1
2
3
4
5
6
7
8
9
    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>() );
    }

其他一些简单明显的包装,如前所述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    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);
    }


插入一个新项目并返回它,对于简单的移动语义(如push_back(c).swap(value)和相关案例)很有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}

弹出并返回项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}


IMO需要更多的pair功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#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
template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }

如果您需要使用很多采用指针+字节数的函数,它总是

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

用*复制字符串:

1
2
3
4
5
6
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();
}

用于std::vector的无序擦除。从vector中删除元素的最有效方法,但它不保留元素的顺序。我没有看到将它扩展到其他容器的意义,因为大多数容器对于从中间移除项目没有相同的惩罚。它类似于已经发布的其他一些模板,但它使用std::swap来移动项目,而不是复制。

1
2
3
4
5
6
7
8
9
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表示正。

1
2
3
4
5
template <typename T>
int signum(T val)
{
    return (val > T(0)) - (val < T(0));
}

CLAMP是一个非常简单的解释,它对一个值进行钳制,使其位于给定的范围内。标准库包括minmax,但不包括clamp,这让我很困惑。

1
2
3
4
5
template<typename T>
T clamp(const T& value, const T& lower, const T& upper)
{
    return value < lower ? lower : (value > upper ? upper : value);
}

我最喜欢的是Transposer,它找到一个大小相同的容器元组的转置。也就是说,如果你有一个tuple,vector>,它会把它转换成vector>。在XML编程中很方便。我就是这样做的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
#include <iterator>
#include <vector>
#include <list>
#include
#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,"
"
));

  return 0;
}


不确定这些是否符合标准包装,但我常用的助手函数是:

1
2
3
4
5
6
7
8
9
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
for_each(iseq(vec), do_it());

我重载了所有的算法,使它们只使用一个input_sequence_range<>类型的参数,而不使用两个输入迭代器(在任何不只是输出的东西中输入)。

1
2
3
4
5
6
7
8
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()的工作原理:

1
2
3
4
5
6
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());
}

同样,我也有专门的

  • 迭代器
  • 指针(基元数组)
  • 流迭代器
  • 任何范围[开始,结束]都可以统一使用:对所有内容都使用iseq()。