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

  • 相关阅读:
    2018前端工程师成长路线图
    ECMAScript正则表达式6个最新特性
    Fundebug前端JavaScript插件更新至1.2.0
    写给前端工程师的10条实用原则
    代码面试需要知道的8种数据结构(附面试题及答案链接)
    20个Chrome DevTools调试技巧
    Web应用架构入门之11个基本要素
    配置Tree Shaking来减少JavaScript的打包体积
    SQL Server全文搜索(转载)
    ASP.NET Core多语言 (转载)
  • 原文地址:https://www.cnblogs.com/lzxskjo/p/4887848.html
Copyright © 2020-2023  润新知