用C++11实现的assign功能

assign是boost 的一个对容器赋值的库,可以用非常简洁甚至看起来不像合法C++代码的方式对容器操作,详情可参考罗剑锋的《boost指南》。

下面是我自己实现的代码:

  1 #ifndef ASSIGN_H
  2 #define ASSIGN_H
  3 
  4 #include <functional>
  5 #include <type_traits>
  6 #include <iterator>
  7 //#include <boost/concept_check.hpp>
  8 #include <vector>
  9 #include <set>
 10 //  …………
 11 //  其它容器
 12 
 13 //using namespace boost;
 14 
 15 namespace li
 16 {
 17 template<class T, class U>
 18 constexpr bool is_same_v = std::is_same<T,U>::value;
 19 template<class Con>
 20 using ct = typename Con::value_type;
 21 template<class II>
 22 using it = typename std::iterator_traits<II>::value_type;
 23 
 24 template<class Con>
 25 class list_insert
 26 {
 27 //BOOST_CONCEPT_ASSERT((Container<Con>));
 28 public:
 29     using T = ct<Con>;
 30     using Function = std::function<void(T)>;
 31     using Generate = std::function<T()>;
 32 
 33     list_insert() = delete;
 34     list_insert(Function fun) :insert_(fun){}
 35     ~list_insert() = default;
 36 
 37     list_insert& operator,(const T& val)
 38     {
 39         insert_(val);
 40         return *this;
 41     }
 42     list_insert& operator()(const T& val)
 43     {
 44         insert_(val);
 45         return *this;
 46     }
 47     list_insert& repeat(std::size_t n, const T& val)
 48     {
 49         while(n--) insert_(val);
 50         return *this;
 51     }
 52     list_insert& repeat_fun(std::size_t n, Generate gen)
 53     {
 54         while(n--) insert_(gen());
 55         return *this;
 56     }
 57     template<class II>
 58     list_insert& range(II first, II last)
 59     {
 60     //    BOOST_CONCEPT_ASSERT((InputIterator<II>));
 61         static_assert((is_same_v<it<II>,T>)  //等价于 is_same<typename iterator_traits<II>::value_type,T>::value
 62                       ,"插入元素的类型必须和容器的元素类型一致!");
 63         for(; first != last; ++first)
 64             insert_(*first);
 65         return *this;
 66     }
 67 private:
 68     Function insert_;
 69 };
 70 
 71 template<class Con>
 72 inline list_insert<Con> push_back(Con &c)
 73 {
 74 //    BOOST_CONCEPT_ASSERT((BackInsertionSequence<Con>));
 75     return list_insert<Con>(
 76         [&c](const ct<Con> &val)
 77         {
 78             c.push_back(val);
 79         }
 80     );
 81 }
 82 template<class Con>
 83 inline list_insert<Con> push_front(Con &c)
 84 {
 85 //    BOOST_CONCEPT_ASSERT((FrontInsertionSequence<Con>));
 86     return list_insert<Con>(
 87         [&c](const ct<Con> &val)
 88         {
 89             c.push_front(val);
 90         }
 91     );
 92 }
 93 template<class Con>
 94 inline list_insert<Con> insert(Con &c)
 95 {
 96 //    BOOST_CONCEPT_ASSERT((AssociativeContainer<Con>));
 97     return list_insert<Con>(
 98         [&c](const ct<Con> &val)
 99         {
100             c.insert(val);
101         }
102     );
103 }
104 
105 template<class T>
106 inline list_insert<std::vector<T>> operator+=(std::vector<T> &c, const T &val)
107 {
108     return push_back(c)(val);
109 }
110 template<class T>
111 inline list_insert<std::set<T>> operator+=(std::set<T> &c, const T &val)
112 {
113     return insert(c)(val);
114 }
115 //  …………
116 //  其它容器
117 }
118 #endif // ASSIGN_H

下面是测试代码:

 1 #include <iostream>
 3 #include "assign.hpp"
 4 #include <algorithm>
 5 #include <string>
 6 #include <vector>
 7 #include <list>
 8 #include <set>
 9 using namespace std;
11 using namespace li;
12 
13 template<class container>
14 void print(container c)
15 {
16     for(auto &val : c)
17         cout << val << ' ';
18     cout << endl;
19 }
20 int main()
21 {
22     int arr[] = {1,2,3,4,5,6,7,8};
23     vector<int> v;
24     list<string> l;
25     set<double> s;
26 
27     push_back(v)(5)(3)(6)(9).repeat(2,4).repeat_fun(3,&rand).range(arr+2, arr+5);
28 //  等价于
29 //    v.insert(v.end(), {5,3,6,9});
30 //    fill_n(back_inserter(v), 2,4);
31 //    generate_n(back_inserter(v), 3,&rand);
32 //    v.insert(v.end(), arr+2, arr+5);
33     push_front(l)("dog")("cat").repeat(2,"fish");
34     s += 2.3, 4.6, 1.8, 54.9, 34, 87;
35 
36     print(v);
37     print(l);
38     print(s);
39 }

说说自己的心得吧:最初assign最爽的地方是能一次插入很多个元素(虽然本质上是调用了n次插入),在C++11推出列表初始化与列表插入(见测试代码 line29)后,这一部分算是追赶上来了;assign库的其它操作也都能在STL里找到对应的操作。但是,assign可以把这些连起来用,6到不行,这是STL无法比拟的。

实现的时候用了模板变量、类型别名、lambda表达式、类型特征(type_traits)等新特性,还有一个略鸡肋的概念检查。

先说说类型别名吧,它实现的就是typedef 的功能,但是比typedef 更强大,可以支持模板,算得上是“模板类型”。使用它可以简化模板元编程,详情可见SM的《Effective Modern C++》。

模板变量是C++14新推出的特性,它有三种用法

template <class T>
T PI = T(3.1415926535897932385);

template <class T>
constexpr bool is_integral_v = is_integral<T>::value;

template <size_t N>
int fib = fib<N-1> + fib<N-2>;
template<>
int fib<1> = 1;
template<>
int fib<0> = 0;

第一种是正常的用法,第二种是我受SM启发想到的用法,谁说模板变量就不可以是固定的类型了?它和模板类型结合起来,可以大大地简化模板元编程,例如实现代码 line 61。第三种用法就纯属脑洞大开了,我用两种最新的编译器gcc 5.1.0和clang 3.7.0 都无法支持这种用法。

type_traits,顾名思义,就是类型特征,有点类似 limits,不同的是 limits是数字的特征,而type_traits是类型的特征,主要用于元编程。

static_assert,是一种编译期的assert。原来的assert是在运行期,如果不符合就结束程序;而static_assert是编译不能通过,明显要好很多。

functional 是对函数的泛型,它支持一切“可调用物”,函数、函数对象都可以。顺便说一句,函数和函数对象毕竟不是同一种东西,如果对它们调用 is_class和 is_function,得到的结果是截然相反的。本例中 list_insert的构造函数的参数就只能是对象,不能是函数。因为 insert_需要绑定到一个容器,对象可以使用成员数据保存对容器的引用,而函数就无法做到这一点。

lambda表达式是尤其方便的新特性,可以称的上是规则改变者。它可以就地创建匿名对象,兼顾效率与方便,语法是:[捕获列表](参数){函数体}。实现代码中的98-101行就是一个lambda表达式,它等价于:

 1 template< class C >
 2 class call_insert
 3 {
 4     C& c_;
 5 public:
 6     call_insert( C& c ) : c_( c )
 7     { }
 8     
 9     template< class T >
10     void operator()( T r ) 
11     {
12         c_.insert( r );
13     }
14 };
15 
16 return list_insert<Con>(call_insert<Con>(c));

是不是明显要简单好多?

概念检查是boost库的一个功能,就是概念检查。比如检查某变量是否是容器,是否是迭代器等。不过我觉得不是很有必要,国为它引起的错误信息简直不是人看的,又臭又长,反倒是不用的时候要清爽很多。

说到这个库用了什么设计模式,策略模式嘛,太明显了,function就适合实现这个模式。

我只实现了assign库的一些基本功能,还剩下的就不实现了,毕竟不要重复发明轮子,只是锻炼一下能力。

原文地址:https://www.cnblogs.com/lzxskjo/p/4887848.html