zoukankan      html  css  js  c++  java
  • Concept check

    闲话小叙:C++11的发布让C++迎来了2.0时代,引入了很多有用的新特性。然而这是一个隔了10多年才发布的2.0版本,其他的语言都发布N代了,所以C++11的一些新特性对别的语言来说已经是旧特性了,一点都不奇怪。比如auto、regex、lambda、tuple等等。幸而C++标准委员会已经决定要以三年一小改六年一大改的速度更新了,所以下一个时代的C++就是C++17,里面会有什么令人期待的让C++重新领先的理念呢。也许概念检查(Concept Check)就是其中一个吧。

    概念检查是C++模板编程中很有用的工具,可以把错误从运行期提前到编译期。Boost以库的方式提供了这个功能,比如我要写一个针对容器的函数,就要这样用:

    template<class Con>
    void print(Con C)
    {
        BOOST_CONCEPT_ASSERT((Container<Con>));
        .....
    }

    当传入的类型不符合std容器的定义时,就会编译报错。研究一下它的源代码,发现是这样的:

     1 BOOST_concept(Container,(C))
     2     : Assignable<C>
     3   {
     4     typedef typename C::value_type value_type;
     5     typedef typename C::difference_type difference_type;
     6     typedef typename C::size_type size_type;
     7     typedef typename C::const_reference const_reference;
     8     typedef typename C::const_pointer const_pointer;
     9     typedef typename C::const_iterator const_iterator;
    10 
    11       BOOST_CONCEPT_USAGE(Container)
    12       {
    13           BOOST_CONCEPT_ASSERT((InputIterator<const_iterator>));
    14           const_constraints(c);
    15       }
    16 
    17    private:
    18       void const_constraints(const C& cc) {
    19           i = cc.begin();
    20           i = cc.end();
    21           n = cc.size();
    22           n = cc.max_size();
    23           b = cc.empty();
    24       }
    25       C c;
    26       bool b;
    27       const_iterator i;
    28       size_type n;
    29   };

    其本质就是检查一下传入的类型有没有定义value_type、size_type等类型,然后调用一下begin()、end()等函数。

    原理并不复杂,然而一旦出错,那错误信息简直是乱七八遭。因此C++17的提案里虽然有概念检查,但并不是基于Boost的方式,它重新设计,然后以语法的方式实现了。同样的理念,以语法的方式实现是坠吼的了。

    新的概念检查可以见这里:http://en.cppreference.com/w/cpp/language/constraints。

    通过它,就可以写出下面这样的代码,非常酷。第一眼看到时简直不敢相信这还是C++!

    auto f(Container) -> Sortable; 
    
    Sortable x = f(y); 
     
    void g1(const EqualityComparable*, Incrementable&);
    // 隐式模板,等价于:
    // template<EqualityComparable T, Incrementable U>
    // void g1(const T*, U&);

    程序员可以定义一些概念,比如(Sortable、Numeric),然后把它们像"typename"一样用。不同的是typename指代任何类型,而这些概念只代表符合某种要求的类型,当不符合时会编译不通过。而且诊断信息会更明确,比如:

    std::list<int> l = {3,-1,10};
    std::sort(l.begin(), l.end()); 
    //Typical compiler diagnostic without concepts:
    //  invalid operands to binary expression ('std::_List_iterator<int>' and
    //  'std::_List_iterator<int>')
    //                           std::__lg(__last - __first) * 2);
    //                                     ~~~~~~ ^ ~~~~~~~
    // ... 50 lines of output ...
    //
    //Typical compiler diagnostic with concepts:
    //  error: cannot call std::sort with std::_List_iterator<int>
    //  note:  concept RandomAccessIterator<std::_List_iterator<int>> was not satisfied

    使用concepts后编译器会直接告诉你std::list的Iterator不符合随机迭代器的要求。

    我看到后兴奋地与果粉讨论,他问:“如果传给Numeric的是自定义类呢?”。我一愣,说:“可以让自定义类定义类型转换运行符operator double()啊!”。这才意识到概念检查毕竟还没有发布,还是有一些需要完善的地方。仔细一想,如果能在定义类的时候也用上概念检查就好了,比如:

    template<class T>
    class my_num : Comparable,Numeric
    {
    //    实现operator= 与 operator<
       // 实现operator double
    }

    这看起来很像接口,没错,就是这样,也许C++应该引入接口了。而且事实上C++标准里已经有东西很像接口了,比如enable_shared_from_this。

  • 相关阅读:
    剑指 Offer 50. 第一个只出现一次的字符
    剑指 Offer 42. 连续子数组的最大和
    剑指 Offer 41. 数据流中的中位数
    剑指 Offer 40. 最小的k个数
    剑指 Offer 39. 数组中出现次数超过一半的数字
    剑指 Offer 38. 字符串的排列
    MySQL更改密码
    WPF中的MySQLHelper
    WPF多线程
    mysql-5.7.28-winx64(压缩包)安装教程
  • 原文地址:https://www.cnblogs.com/lzxskjo/p/5285766.html
Copyright © 2011-2022 走看看