您使用哪些C++标准库包装函数?

81 c++

今天早上问的这个问题让我想知道你认为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;
}
Run Code Online (Sandbox Code Playgroud)

这个用于清除(或多或少)任何类型 - 特别适用于像std :: stack这样的东西:

template <class C>
void Clear( C & c ) {
    c = C();
}
Run Code Online (Sandbox Code Playgroud)

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

Vik*_*ehr 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);
}
Run Code Online (Sandbox Code Playgroud)

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; 
}
Run Code Online (Sandbox Code Playgroud)

(在pod类型的向量(int,float等)的情况下,几乎所有对象都被删除,std :: remove可能更快).

  • @kts:当您知道*container*已排序时,只需直接调用*binary\_search*即可. (5认同)
  • 是否有人认为包含需要第三个模板(`bool sorted = false`)和`sorted == true`时调用`binary_search`而不是`find`? (4认同)
  • boost :: array STL等价物在最近的编译器(甚至是codewarrior)的tr1命名空间中可用:std :: tr1 :: array <> (2认同)

sbk*_*sbk 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();
}
Run Code Online (Sandbox Code Playgroud)

  • 在C++ 0x中,`v [index] = st :: move(v.back()); v.pop_back();`和它一样高效. (12认同)

sbi*_*sbi 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&);
};
Run Code Online (Sandbox Code Playgroud)

好吧,因为它似乎并不像我想象的那样直截了当,这里有一个解释:
在它的构造函数中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
}
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

#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);
}
Run Code Online (Sandbox Code Playgroud)

但它删除了它在课堂上使用的能力.


这是实现相同效果的第三种方式(不会遇到可能抛出析构函数的问题):

执行:

//none -- it is built into the language
Run Code Online (Sandbox Code Playgroud)

用法:

#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);
}
Run Code Online (Sandbox Code Playgroud)


Gle*_*len 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;
}
Run Code Online (Sandbox Code Playgroud)

  • @Roger Pate,是的,我知道,这就是为什么答案从"不是真正的包装,但......"开始的原因. (10认同)
  • 不回答问题,不是stdlib的包装器. (2认同)

sbi*_*sbi 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;}
Run Code Online (Sandbox Code Playgroud)

  • @Roger:它包装数组,以便与标准库一起使用.你去吧 `:)` (4认同)
  • 我也有这些.:) +1对于它的价值,你只需要两个(沟通const版本).当数组是const时,`T`将是`const U`并且你得到了预期的函数. (2认同)
  • @Stacked,@ sbe:无论是否有`&`,数组都不会通过值传递.`&`用于启用数组长度的类型推导. (2认同)

Inv*_*rse 12

有时候我觉得我在begin()end()地狱.我想要一些功能,如:

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

和其他类似的std::find,std::for_each基本上所有的STL算法.

我觉得sort(x)阅读/理解要快得多sort(x.begin(), x.end()).

  • [Boost.Range](http://www.boost.org/doc/libs/release/libs/range/index.html)v2为整个标准库提供了这样的适配器. (4认同)

Jon*_*rdy 9

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

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

当我记住它们时会更新更新.:P

  • @BillyONeal:这就是我不再使用它的原因了.@Steve:这就是我仍然使用它的原因. (11认同)
  • 呵呵 - 一种"boost :: lexical_cast <t,t>"的快捷方式. (3认同)

MSa*_*ers 9

当然,每个人工具箱中的效用函数copy_if.虽然不是真正的包装.

我经常使用的另一个帮助是deleter,我用一个函子std::for_each来删除容器中的所有指针.

[编辑]挖掘我的"sth.h"我也发现了 vector<wstring> StringSplit(wstring const&, wchar_t);

  • @公司愚蠢的不幸受害者:获得内部图书馆的例外,然后将Boost的有用部分导入内部图书馆.即使在政治中,所有问题都可以通过另一层次的间接来解决. (7认同)
  • @Matthieu M.不幸的是,我们所有人都不能使用助推器. (2认同)

Eva*_*ran 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 那个`split()`吞下`std :: getline()`中发生的任何错误,静默地返回一个太短的向量. (2认同)
  • @sbi:你的评论让我对利用`stringstream` /`getline`循环实际可能出错(除了字符串根本没有足够的令牌)的兴趣达到了顶峰.我在这里提出了一个问题:http://stackoverflow.com/questions/2562906/ways-stdstringstream-can-set-fail-bad-bit (2认同)
  • @Evan:我的立场得到了纠正.请参阅http://stackoverflow.com/2563542#2563542上的评论.抱歉. (2认同)

Mat*_* M. 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
Run Code Online (Sandbox Code Playgroud)

  • 唯一的问题是它打破了STL中预期的语义擦除 - 删除习语.你需要使用删除语义的*any*算法删除删除习惯用法 - 而不仅仅是`std :: remove`.例如,`std :: unique`. (2认同)

Mik*_*one 7

包装sprintf

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

目标是将格式与输出分离,而不会遇到sprintf及其类似的麻烦.它不漂亮,但它非常有用,特别是如果您的编码指南禁止iostreams.


这是Neil Butterworth根据需要分配的版本.[查看Mike版本的修订历史记录,我将其作为剩余两个版本的子集删除.它类似于Neil,除了后者使用vector而不是delete []是异常安全的:string的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] );
}
Run Code Online (Sandbox Code Playgroud)

这是一个预先确定所需尺寸的版本,来自Roger Pate.这需要可写的std :: strings,它由流行的实现提供,但是C++ 0x明确要求.[查看Marcus版本的修订历史记录,我将其删除,因为它略有不同,但基本上是下面的一部分.-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);
}
Run Code Online (Sandbox Code Playgroud)

公共界面

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, ...);
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 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
Run Code Online (Sandbox Code Playgroud)

是的,我知道,最好否定谓词并使用谓词版本adjacent_find:)

  • @the_drow:非常感谢你这篇有用的评论:)我不是很喜欢它,但这是我工作的要求......我已经摆脱了习惯,不要为我的灵魂担心;) (2认同)