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库的一些基本功能,还剩下的就不实现了,毕竟不要重复发明轮子,只是锻炼一下能力。