【问题标题】:Which C++ Standard Library wrapper functions do you use?您使用哪些 C++ 标准库包装函数?
【发布时间】:2011-02-02 21:58:07
【问题描述】:

This question,今天早上被问到,让我想知道您认为 C++ 标准库中缺少哪些功能,以及您是如何使用包装函数填补空白的。例如,我自己的实用程序库有这个矢量追加功能:

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

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

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

我还有一些,但我对您使用的那些感兴趣?请限制对 wrapper 函数的回答 - 即不超过几行代码。

【问题讨论】:

  • 我包装了大部分 STL 算法以作用于整个容器而不是范围,这算不算,只是因为弄乱迭代器是一个很常见的错误 :)?
  • @Billy 实际上,CW 并不是提出主观问题的真正借口。我将更改标题,这应该会让人们感到高兴。
  • @kts:由于 vector::insert 被赋予了随机访问迭代器,一个好的实现将使用编译时调度来完成。
  • c.swap(C())清空一个容器不是更好吗?
  • @Alexandre:这是不允许的:它将临时绑定到非常量引用。 C().swap(c) 会起作用。

标签: c++


【解决方案1】:

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

Clamp 非常容易解释,它会限制一个值,使其位于给定范围内。令我惊讶的是,标准库包括 minmax 但不包括 clamp

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

【讨论】:

    【解决方案2】:

    boost::array

    包含(container, val)(很简单,但很方便)。

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

    remove_unstable(开始、结束、值)

    一个更快的 std::remove 版本,但它不保留剩余对象的顺序。

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

    (如果是 pod 类型(int、float 等)的向量并且几乎所有对象都被删除,std::remove 可能会更快)。

    【讨论】:

    • sorted==true 调用binary_search 而不是find 时,是否有人认为包含需要第三个模板 (bool sorted=false) 和专业化?
    • @kts:当你知道 container 已排序后,直接调用 binary_search 即可。
    • boost::array STL 等效项在最新编译器(甚至是 codewarrior)的 tr1 命名空间中可用:std::tr1::array
    【解决方案3】:

    用*复制一个字符串:

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

    【讨论】:

      【解决方案4】:
      template <typename T> size_t bytesize(std::vector<T> const& v) { return sizeof(T) * v.size(); }
      

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

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

      【讨论】:

        【解决方案5】:

        不是真正的包装器,而是臭名昭著的失踪copy_if。来自here

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

        【讨论】:

        • 不回答问题,不是标准库的包装。
        • @Roger Pate,是的,我知道,这就是为什么答案以“不是真正的包装器,但是....”开头的原因。
        • @Roger 实现细节。如果你真的希望你可以用remove_copy_if() 来实现它。 :p
        【解决方案6】:
        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);
        }
        

        【讨论】:

        • @Billy:用于稍后自动恢复值。 (并且 val 应该从 ctor 中删除。)
        • 抱歉,我还是迷路了(我是 C++ 的新手),有没有人能把它弄傻?
        • @dreamlax:我在答案中添加了一些描述性文字。现在是否可以理解,还是应该深入了解细节?
        • 哦,第二个很聪明。
        • 嗯,现实生活中的用例是什么?
        【解决方案7】:

        IMO 需要为 pair 提供更多功能:

        #ifndef pair_iterator_h_
        #define pair_iterator_h_
        
        #include <boost/iterator/transform_iterator.hpp>    
        #include <functional>
        #include <utility>    
        
        // pair<T1, T2> -> T1
        template <typename PairType>
        struct PairGetFirst : public std::unary_function<PairType, typename PairType::first_type>
        {
            typename typename PairType::first_type& operator()(PairType& arg) const
            {       return arg.first;   }
            const typename PairType::first_type& operator()(const PairType& arg) const
            {       return arg.first;   }
        };
        
        
        
        // pair<T1, T2> -> T2
        template <typename PairType>
        struct PairGetSecond : public std::unary_function<PairType, typename PairType::second_type>
        {
            typename PairType::second_type& operator()(PairType& arg) const
            {       return arg.second;  }
            const typename PairType::second_type& operator()(const PairType& arg) const
            {       return arg.second;  }
        };
        
        
        
        // iterator over pair<T1, T2> -> iterator over T1
        template <typename Iter>
        boost::transform_iterator<PairGetFirst<typename std::iterator_traits<Iter>::value_type>, Iter> 
        make_first_iterator(Iter i)
        {
            return boost::make_transform_iterator(i, 
                PairGetFirst<typename std::iterator_traits<Iter>::value_type>());
        }
        
        
        
        // iterator over pair<T1, T2> -> iterator over T2
        template <typename Iter>
        boost::transform_iterator<PairGetSecond<typename std::iterator_traits<Iter>::value_type>, Iter> 
        make_second_iterator(Iter i)
        {
            return boost::make_transform_iterator(i, 
                PairGetSecond<typename std::iterator_traits<Iter>::value_type>());
        }
        
        
        
        // T1 -> pair<T1, T2>
        template <typename FirstType, typename SecondType>
        class InsertIntoPair1st : public std::unary_function<FirstType, std::pair<FirstType, SecondType> >
        {
        public:
            InsertIntoPair1st(const SecondType& second_element) : second_(second_element) {}
            result_type operator()(const FirstType& first_element)
            {
                return result_type(first_element, second_);
            }
        private:
            SecondType second_;
        };
        
        
        
        // T2 -> pair<T1, T2>
        template <typename FirstType, typename SecondType>
        class InsertIntoPair2nd : public std::unary_function<SecondType, std::pair<FirstType, SecondType> >
        {
        public:
            InsertIntoPair2nd(const FirstType& first_element) : first_(first_element) {}
            result_type operator()(const SecondType& second_element)
            {
                return result_type(first_, second_element);
            }
        private:
            FirstType first_;
        };
        
        #endif // pair_iterator_h_
        

        【讨论】:

        • 为什么不把PairType模板移到operator()?此外,标识符中的双下划线是保留的。
        • @GMan - 因为那样你就不能使用unary_function,我在代码中的某个时候需要它。至于双下划线,谢谢你告诉我——我需要改变它。
        • 这会错误地使用依赖名称(argument_type,result_type),编译器需要拒绝它。 “在类模板或类模板成员的定义中,如果类模板的基类依赖于模板参数,则在非限定名称查找期间不会在定义的点检查基类范围类模板或成员,或在类模板或成员的实例化期间。” [14.6.2/3, C++03]
        • @Roger Pate:我不知道这条规则。现在已经修好了。
        【解决方案8】:

        类似于人们之前发布的内容,I have 算法的便利重载,用于简化传递迭代器参数。我这样称呼算法:

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

        我重载了所有算法,以便它们采用 input_sequence_range&lt;&gt; 类型的单个参数,而不是两个输入迭代器(输入作为任何不仅仅是输出的东西)。

        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
        • 指针(原始数组)
        • 流迭代器
        • 任何范围 [begin,end) 仅用于统一用途:对所有内容都使用 iseq()

        【讨论】:

        • ...或者您可以只使用 Boost.Range 并获得范围适配器和经过同行评审、经过广泛测试的代码的好处。
        【解决方案9】:

        我似乎需要一个笛卡尔积,例如 {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 必须是 forward iterator 而不是输入迭代器。输入迭代器不适合多次传递。
        【解决方案10】:

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

        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()) 阅读/理解要快得多。

        【讨论】:

        • 提示;使用 'sort(boost::begin(x), boost:end(x));' 代替,你也可以对数组进行排序。
        • Boost.Range v2 为整个标准库提供了这样的适配器。
        【解决方案11】:

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

        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 相同,但会自动调整大小以适应所需的索引。

        【讨论】:

        • 标准库(在所有范围内)为该名称的宏保留名称“assert”。
        • 我认为在 windows 或 mfc 头文件中还声明了一个 assert() 宏。它们都在 WM_PAINT 事件中失败,因为在某些情况下显示断言对话框会触发下一个断言。所以最后,用第三个实现替换那些有缺陷的实现并不是什么大问题。您所要做的就是在#include 之后明确地重新声明自己的assert-macro,或者只使用#undef assert.
        【解决方案12】:

        我最喜欢的一个是Transposer,它可以找到相同大小的容器元组的转置。也就是说,如果您有一个tuple&lt;vector&lt;int&gt;,vector&lt;float&gt;&gt;,它会将其转换为vector&lt;tuple&lt;int, float&gt;&gt;。在 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;
        }
        

        【讨论】:

          【解决方案13】:

          包装 sprintf

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

          目标是将格式与输出分离,而不会遇到 sprintf 及其同类问题。它不漂亮,但非常有用,尤其是在您的编码指南禁止 iostream 的情况下。


          这是一个根据需要分配的版本,来自 Neil Butterworth。 [查看 Mike 版本的修订历史记录,我将其作为其余两个版本的子集删除。它类似于 Neil 的,除了后者通过使用向量而不是 delete[] 是异常安全的:字符串的 ctor 将在分配失败时抛出。 Mike's 还使用稍后显示的相同技术来预先确定尺寸。 -RP]

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

          这是一个预先确定所需大小的版本,来自Roger Pate。这需要可写的 std::strings,它由流行的实现提供,但 C++0x 明确要求。 [查看 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);
          }
          

          公共界面

          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(),它返回如果 n 是无限的则将打印的字符数......”因此使用 0 缓冲区的虚拟调用来测量所需的缓冲区大小。
          • @Checkers:啊,助推器。如此巨大的潜力,他们也不会让我使用。总有一天,希望。无论如何,Boost 已经大到无法完全理解了吗?我很高兴收到boost::spirit
          • 这实际上是 Windows 代码 - 来自 MSDN “对于 _vsnprintf,如果要写入的字节数超过缓冲区,则写入字节数并返回 –1。”但我确实将它移植到 Linux。我不记得我的 Linux 应用程序是否真的使用它,或者我是否已经在那里测试过它的大缓冲区大小 - 必须这样做。谢谢。
          • 如果您仍然使用 Windows 代码,请继续使用_vscprintf 来确定所需的缓冲区大小。
          【解决方案14】:

          我有一个标头,将以下内容放在“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;
          }
          

          【讨论】:

          • split() 吞下std::getline() 中发生的任何错误,默默地返回一个太短的向量。
          • 当然,您应该在检索字符串之前检查结果的size()
          • 我怎么知道结果应该有多少个字符串?
          • @sbi:您的评论激起了我对stringstream/getline 循环实际可能出错的地方的兴趣(除了字符串根本没有足够的令牌来获取)。我在这里提出了一个问题:stackoverflow.com/questions/2562906/…
          • @Evan:我的立场是正确的。请参阅我在stackoverflow.com/2563542#2563542 的评论。对不起。
          【解决方案15】:

          臭名昭著的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(...)
          • 唯一的问题是它破坏了 STL 中预期的语义擦除删除习语。您需要具有删除语义的 any 算法的擦除删除习语——不仅仅是std::remove。例如,std::unique
          • 嗯,我有针对大多数 STL 算法的容器适配 :) 但这是我使用最多的一个,通常如果我想要唯一性,我会使用 set 开头。
          • @Matthieu M.:请记住,这样做会让那些一直在使用 STL 的人在他们的脑海中敲响警钟“警告:删除式算法无需擦除!!”。它确实没有什么问题,但如果我需要在许多程序员之间共享我的代码,我不会这样做。只是我的 2 美分。
          • @Billy:这就是为什么我称它为擦除而不是删除。除了让他们查阅代码之外,我无能为力。值得庆幸的是,使用现代 IDE,只需单击一下即可定义:)
          【解决方案16】:
          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;}
          

          【讨论】:

          • 我也有这些。 :) +1 对于它的价值,你只需要两个(放弃 const 版本)。当数组为 const 时,T 将是 const U,您将获得预期的功能。
          • @GMan:有一些版本的 GCC 不能编译只有非const 版本的代码,这就是const 版本存在的原因。由于这可能是特定 GCC 版本的错误,我将删除它们。
          • @Marcus:这些比 Boost.Range 要老得多。 :)
          • @Roger:它包装了数组以便它们与标准库一起使用。你去吧。 :)
          • @Stacked, @sbe:数组永远不会按值传递,无论是否存在&amp;&amp; 用于启用数组长度的类型推导。
          【解决方案17】:

          插入一个新项目并将其返回,这对于像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;
          }
          

          【讨论】:

            【解决方案18】:

            这是我的一组额外实用程序,建立在您可能需要某些功能的 boost.range'ish 标准算法包装器之上。 (写的很琐碎,这才是有趣的东西)

            #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 for_each_permutation(...),主要是因为我写了一个类似的包装器 =)。但是为什么 erase_duplicates(...) 返回一个有符号整数呢?
            • 嗨维克多!我想你应该在你以前的工作中也看到过 for_each_permutation。 ;) erase_duplicates 返回已擦除元素的数量,这对于记录和调试很有用。
            • 嗯可能浏览过它没有意识到它做了什么;),无论如何,我明白为什么它返回一个整数,我只是不明白为什么整数是有符号的(或更具体地说;为什么它不是无符号的)?
            • 啊。只是我的懒惰:-P。 size_t 是合适的类型。
            【解决方案19】:

            看看我的stl_util.h,许多经典(删除函数,copy_if),还有这一个(可能也很常见,但到目前为止我没有在回复中看到它)用于搜索一个映射并返回找到的值或默认值,在 Python 的 dict 中是 get

            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 不适合使用。

            【讨论】:

            • 如果 V 是 int 或 float 或其他原语怎么办?
            • 空值初始化适用于 C++ 中的基本类型。对于整数和浮点数,这将使默认的 null_result 为 0。
            【解决方案20】:

            我会用它的名字来调用这样一个附加函数,并会使用 operator+= , operator*= 等等来进行元素操作,例如:

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

            之前暗示的其他一些简单而明显的包装器:

                template<typename X> inline void sort_and_unique(std::vector<X> &vec)
                {
                    std::sort( vec.begin(), vec.end() );
                    vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
                }
            
            
                template<typename X> inline void clear_vec(std::vector<X> &vec)
                {
                    std::vector<X>().swap(vec);
                }
            
            
                template<typename X> inline void trim_vec(std::vector<X> &vec, std::size_t new_size)
                {
                    if (new_size<vec.size())
                        std::vector<X>(vec.begin(),vec.begin() + new_size).swap(vec);
                    else
                        std::vector<X>(vec).swap(vec);
                }
            

            【讨论】:

            • 这些运算符是一个很好的例子,说明为什么应该很少进行运算符重载。我会认为,vec+=val 会将值 追加 到向量。 (见stackoverflow.com/questions/2551775/.)既然我已经看到了你的实现,我认为这也是对+=含义的正确解释。我不知道哪个是对的还是错的,所以我们没有+=std::vector 可能是一样的。
            • @sbi 我同意。我发现operator+() 缺少对该标准的惊人早期洞察力。我通常希望在任何我看到加号运算符的地方都有一个 O(1) 操作。 C++ 使昂贵或危险的事情变得更加冗长或难以完成,我喜欢这种方式。看看 Java:最严重的编码错误之一就是滥用加号运算符。当然,话又说回来,C++ 并不总是让廉价和快速的事情变得容易,但是,嘿。优秀的 C++ 程序员非常注重性能。 ;)
            • 我同意你们俩的观点,op+() 不应该被定义,因为它不明确。但是向量通常是(数学)向量空间的一部分,并且存在添加两个向量和标量乘法的规范定义。进一步说明您的论点:一个简单的double 也是一个向量,因此如果您添加两个double 变量,例如a+b,那么您会期望得到一个新的double 而不是pair(a,b) 这样的双倍。与标量相乘也是规范的,但将两个向量相乘则不是。所以重载应该小心..
            【解决方案21】:
            //! \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 用于更复杂的情况)
            • 我不明白,为什么 assert()?
            • @Viktor:确保reverse_map 中没有重复的键。考虑 map 有 (1->"one"; 2->"one") reverse_map 将获得一个元素 ("one"->1)。断言会抓住这一点。另见:双射
            • 顺便说一句,sbi,一旦你用 NDEBUG 编译并且 assert() 被完全剥离,在 assert() 中包含带有副作用的代码会非常糟糕。
            • gah,更新后我的第一条评论看起来真的很愚蠢,stackoverflow 在谷歌搜索我的名字时排名第一,所以我希望未来的雇主不会看到这个 =)
            【解决方案22】:

            大家工具箱里的实用函数当然是copy_if。虽然不是真正的包装器。

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

            [编辑] 挖掘我的“sth.h”我还发现了vector&lt;wstring&gt; StringSplit(wstring const&amp;, wchar_t);

            【讨论】:

            • +1 用于删除函子。我的删除器仿函数适用于大多数容器,但是我一直在尝试让它与 std::map 一起使用,其中键或值都是指针。我尝试使用类型特征来解决问题,但由于时间限制并没有真正走得太远。您解决了这个问题还是认为这是一个问题?
            • @Matthieu M. 不幸的是,并非所有人都可以使用 boost。
            • @Glen:所有这些问题都可以通过使用智能指针来解决,最好(但不一定)来自 boost。作为一个重要的副作用,带有指向动态分配对象的指针的容器也突然变得异常安全。
            • @Glen 在这里,这里用于规定除了 STD 之外没有库的项目,包括 Boost 或 TR1。
            • @企业愚蠢的不幸受害者:获得内部库的例外,然后将 Boost 的有用部分导入内部库。即使在政治中,所有问题都可以通过另一个层次的间接来解决。
            【解决方案23】:

            我经常将向量用作一组没有特定顺序的项目(显然,当我不需要快速 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();
            }
            

            【讨论】:

            • 好一个。没想到那样做.. :) 应该有一个“Bag”包装器(类似于堆栈和队列),当顺序不重要时可以加速这些操作。
            • 在 C++0x 中,v[index] = st::move(v.back()); v.pop_back(); 的效率差不多。
            • @Matthieu:看看上面的时间戳。 :P 我注意到几个小时后,远远超过了允许的编辑时间。
            • @GMan:你确定吗?在我看来, v.pop_back() 可能会导致未定义的行为,因为将调用析构函数。
            • @Viktor:移动语义并不意味着“我拿走你的资源,就是这样”,它们的意思是“我拿走你的资源,让你处于无资源状态。”换句话说,你的移动语义需要确保对象可以安全地破坏;很容易做到,只需将指针设置为 null。
            【解决方案24】:

            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 :)

            【讨论】:

            • 只要您只在assert() 中进行测试:p
            • 你不应该使用匈牙利符号。
            • @the_drow: 非常感谢你的有用评论 :) 我不太喜欢它,但这是我工作的必要条件......我已经摆脱了这个习惯,不要为我的灵魂担心;)
            【解决方案25】:

            绝对是boost::addressof

            【讨论】:

            【解决方案26】:

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

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

            当我记得他们时会更新更多。 :P

            【讨论】:

            • 呵呵——有点像boost::lexical_cast&lt;t, t&gt;的快捷方式。
            • 是的,如果您不想在项目中加入提升功能,那就太好了
            • @BillyONeal:这就是我不再使用它的原因。 @Steve:这就是我仍然使用它的原因。
            • char*std::string 上调用它会不必要地昂贵。也许模板专业化是为了?
            • 如果我没记错的话,boost::lexical_cast 有很多这样的专业化和错误检查。但是,要对奇数进行字符串化,这很好。
            猜你喜欢
            • 2012-08-09
            • 1970-01-01
            • 2014-06-04
            • 1970-01-01
            • 2012-08-09
            • 1970-01-01
            • 2023-01-16
            • 1970-01-01
            相关资源
            最近更新 更多