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

  • 相关阅读:
    ....
    CodeForces 375A(同余)
    POJ 2377 Bad Cowtractors (最小生成树)
    POJ 1258 AgriNet (最小生成树)
    HDU 1016 Prime Ring Problem(全排列)
    HDU 4460 Friend Chains(bfs)
    POJ 2236 Wireless Network(并查集)
    POJ 2100 Graveyard Design(尺取)
    POJ 2110 Mountain Walking(二分/bfs)
    CodeForces 1059B Forgery(模拟)
  • 原文地址:https://www.cnblogs.com/lzxskjo/p/4887848.html
Copyright © 2011-2022 走看看