Boost C++ 库

……世界上最受推崇且设计最专业的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码规范

概念检查库 - Boost C++ 函数库

Boost 概念检查库 (BCCL)

概念检查库 (Concept Check library) 允许用户按照提议的 C++ 语言扩展风格,对概念 (concepts) 进行显式声明和检查。

概要

C++ 泛型编程的特点是使用模板参数来表示抽象数据类型(或称“概念”)。然而,C++ 语言本身并没有为类或函数模板的编写者提供一种机制,来明确声明用户提供的模板参数应当遵循(或符合)什么概念。通常会将模板参数命名为它们所需遵循的概念,以此作为给用户的提示,并使代码中的概念需求显式化。但是,编译器并不以特殊方式对待这些特殊名称:对编译器而言,名为 RandomAccessIterator 的参数与名为 T 的参数没有任何区别。此外,

Boost 概念检查库提供了:

这些机制使用标准 C++,且不会引入运行时开销。使用该机制的主要代价在于编译时间。

每个编写类或函数模板的程序员都应将概念检查作为代码编写日常的一部分。 应当为组件公共接口中的每个模板参数插入一个概念检查。如果该概念属于标准库,则只需使用 BCCL 中对应的概念检查类。如果不是,则编写一个新的概念检查类——毕竟,它们通常只有几行代码。对于新的概念,还应该创建一个匹配的原型类,它是该概念的最小骨架实现。

本文档分为以下几个部分:

  1. 介绍
  2. 动机示例
  3. 历史
  4. 出版物
  5. 致谢
  6. 使用概念检查
  7. 创建概念检查类
  8. 概念覆盖与原型
  9. 使用概念进行编程
  10. 实现
  11. 参考

Jeremy Siek 贡献了此库。Beman Dawes 管理了正式评审。Dave Abrahams 贡献了重写版本,更新了语法,使其与提议中的 C++ 核心语言概念支持语法更加兼容。

介绍

概念 (concept) 是一组要求(有效表达式、关联类型、语义不变量、复杂度保证等),类型必须满足这些要求才能作为泛型算法调用的参数被正确使用。在 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++ 程序员来说,错误可能显而易见,但对于新手来说,该消息难以理解的原因有几个:

  1. 错误消息与 LessThanComparable 的文档化要求之间没有文本相关性。std::stable_sort()(接上文)
  2. 错误消息过长,列出了用户不知道(也不应该知道!)的 STL 内部函数(例如 __insertion_sort)。
  3. 由于错误消息中列出了太多的库内部函数,程序员很容易推断出问题出在库里,而不是他们自己的代码中。

以下是我们期望看到的更具信息量的消息示例(实际上也是 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’

该消息纠正了标准错误消息的几个缺点。

历史

该概念检查系统的第一个版本由 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.