概念检查库允许人们以提议的 C++ 语言扩展的风格,显式声明和检查概念。
C++ 中的泛型编程的特点是使用模板参数来表示抽象数据类型(或“概念”)。 然而,C++ 语言本身没有为类或函数模板的编写者提供一种机制来显式声明用户提供的模板参数应该建模(或符合)的概念。 模板参数通常以它们需要建模的概念命名,以此作为给用户的提示,并在代码中显式地表达概念要求。 然而,编译器并没有特殊对待这些特殊名称:名为 RandomAccessIterator
的参数与名为 T
的参数在编译器看来没有什么不同。 此外,
Boost 概念检查库提供
这些机制使用标准 C++,并且不引入运行时开销。 使用该机制的主要成本在于编译时。
每个编写类或函数模板的程序员都应该将概念检查作为其代码编写例程的正常部分。 应该为组件公共接口中的每个模板参数插入概念检查。 如果概念是标准库中的概念之一,则只需在 BCCL 中使用匹配的概念检查类。 如果不是,则编写一个新的概念检查类 - 毕竟,它们通常只有几行代码。 对于新概念,还应该创建一个匹配的原型类,它是概念的最小骨架实现
本文档分为以下部分。
Jeremy Siek 贡献了这个库。 Beman Dawes 管理了正式审查。 Dave Abrahams 贡献了一个重写版本,更新了语法,使其更兼容于为 C++ 核心语言提议的概念支持语法。
概念是一组要求(有效的表达式、关联类型、语义不变性、复杂度保证等),类型必须满足这些要求才能在调用泛型算法时被正确用作参数。 在 C++ 中,概念由函数模板(泛型算法)的形式模板参数表示。 然而,C++ 没有显式机制来表示概念——模板参数仅仅是占位符。 按照惯例,这些参数被赋予与所需概念相对应的名称,但当模板参数绑定到实际类型时,C++ 编译器不会强制执行对概念的遵守。
自然地,如果使用不满足概念的至少语法要求的类型调用泛型算法,则会发生编译时错误。 然而,此错误本身不会反映类型未满足概念的所有要求这一事实。 相反,错误可能发生在实例化层次结构的深处,在表达式对于类型无效的点,或者在假定的关联类型不可用的点。 产生的错误消息在很大程度上信息量不足,而且基本上难以理解。
所需的是一种在实例化点(或接近实例化点)强制执行“概念安全”的机制。 Boost 概念检查库使用一些标准 C++ 构造来强制执行早期概念符合性,并在不符合性时提供更具信息性的错误消息。
请注意,此技术仅解决概念的语法要求(有效表达式和关联类型)。 我们不解决语义不变性或复杂度保证,它们也是概念要求的一部分。
我们提供一个简单的示例来说明模板库的不正确用法以及由此产生的错误消息。 在下面的代码中,泛型std::stable_sort()算法来自标准模板库 (STL) [3, 4,5] 应用于链表。
bad_error_eg.cpp: 1 #include <vector> 2 #include <complex> 3 #include <algorithm> 4 5 int main() 6 { 7 std::vector<std::complex<float> > v; 8 std::stable_sort(v.begin(), v.end()); 9 }
在这里,std::stable_sort()算法的原型如下
template <class RandomAccessIterator> void stable_sort(RandomAccessIterator first, RandomAccessIterator last);
尝试使用 Gnu C++ 编译此代码会产生以下编译器错误
/usr/include/c++/4.1.2/bits/stl_algo.h: In function ‘void std:: __insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float >*, std::vector<std::complex<float>, std::allocator<std::complex< float> > > >]’: /usr/include/c++/4.1.2/bits/stl_algo.h:3066: instantiated from ‘void std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx:: __normal_iterator<std::complex<float>*, std::vector<std::complex< float>, std::allocator<std::complex<float> > > >]’ /usr/include/c++/4.1.2/bits/stl_algo.h:3776: instantiated from ‘void std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float >*, std::vector<std::complex<float>, std::allocator<std::complex< float> > > >]’ bad_error_eg.cpp:8: instantiated from here /usr/include/c++/4.1.2/bits/stl_algo.h:2277: error: no match for ‘operator<’ in ‘__val < __first. __gnu_cxx::__normal_iterator< _Iterator, _Container>::operator* [with _Iterator = std::complex<float >*, _Container = std::vector<std::complex<float>, std::allocator< std::complex<float> > >]()’
在这种情况下,根本错误在于std:complex<float>不符合 LessThanComparable 概念。 不幸的是,错误消息中没有任何内容向用户表明这一点。
对于具有足够模板库经验的 C++ 程序员来说,该错误可能很明显,但有几个原因导致初学者可能难以理解此消息
__insertion_sort
),用户不(并且不应该!)知道或关心这些函数。以下是我们可能期望从更具信息性的消息中获得的一个示例(实际上是 Boost 概念检查库产生的)
boost/concept_check.hpp: In destructor ‘boost::LessThanComparable<TT>::~ LessThanComparable() [with TT = std::complex<float>]’: boost/concept/detail/general.hpp:29: instantiated from ‘static void boost:: concepts::requirement<Model>::failed() [with Model = boost:: LessThanComparable<std::complex<float> >]’ boost/concept/requires.hpp:30: instantiated from ‘boost::_requires_<void (*)(boost::LessThanComparable<std::complex<float> >)>’ bad_error_eg.cpp:8: instantiated from here boost/concept_check.hpp:236: error: no match for ‘operator<’ in ‘((boost:: LessThanComparable<std::complex<float> >*)this)->boost:: LessThanComparable<std::complex<float> >::a < ((boost:: LessThanComparable<std::complex<float> >*)this)->boost:: LessThanComparable<std::complex<float> >::b’
此消息纠正了标准错误消息的几个缺点。
std::stable_sort
。
concept_check.hpp
在错误消息中的出现提醒用户,错误在于用户代码,而不是库实现。此概念检查系统的第一个版本是由 Jeremy Siek 在 SGI 的 C++ 编译器和库组工作时开发的。 该版本现在是 SGI STL 发行版的一部分。 最初作为 boost 概念检查库引入的系统与 SGI STL 中的概念检查的不同之处在于,概念检查类的定义大大简化了,但代价是错误消息中的措辞不太有帮助。 2006 年,Dave Abrahams 重写了该系统(保留了向后兼容性),使其更易于使用,更类似于为 C++ 核心语言提议的概念支持,并提供更好的错误消息。
使用函数指针来引起实例化的想法归功于 Alexander Stepanov。 我们不确定使用表达式来预先检查模板的想法的起源,但它确实出现在 D&E[ 2] 中。 感谢 Matt Austern 对 STL 概念的出色文档和组织,这些概念检查都基于此。 感谢 Boost 成员的有用意见和评论。
版权所有 © 2000 | Jeremy Siek(jsiek@osl.iu.edu) Andrew Lumsdaine(lums@osl.iu.edu), 2007 David Abrahams. |