闲话小叙: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。