检查模板定义
这些手动解决方案的概念的一个很大区别是概念允许对模板的定义进行类型检查,而无需做任何特殊的事情。概念检查库只允许对它的 *use* 进行类型检查(除非您手动编写测试实例化类型或为标准情况使用提供的类型,见下文)。例子:
template<typename InputIterator>
int distance(InputIterator a, InputIterator b)
{ return b - a; }
您现在可以在该模板中添加概念检查和特征,但在编写该模板后您将永远不会出错 - 因为标准允许编译器延迟编译模板直到实例化。为了检查,您必须编写“原型”类,其中包含接口所需的操作,然后人工实例化它们。
阅读 BCCL 的文档后,我发现它已经包含了常见的原型,例如“默认可构造”。但是,如果您编写自己的概念,则还必须提供自己的原型,这并不容易(您必须准确找到类型必须提供的最小功能)。例如,如果您的原型包含 operator-,那么使用该(不正确的)原型测试您的模板将会成功,尽管这些概念不需要这样的运算符。
被拒绝的概念提案会根据指定和隐含的要求自动为您创建原型(例如,参数中使用的指针类型 T* 将暗示 T 的 PointeeType 要求)。您不必关心这些东西 - 当然,除非您的模板定义包含类型错误。
检查语义要求
考虑这段代码,使用假设的概念检查
template<ForwardIterator I>
void f(I a, I b) {
// loop two times!
loopOverAToB(a, b);
loopOverAToB(a, b);
}
BCCL 手册说语义要求是不检查的。仅检查语法要求和类型。考虑一个前向迭代器:存在可以在多遍算法中使用它的语义要求。只有语法检查无法测试此要求(想想如果流迭代器意外通过该检查会发生什么!)
在被拒绝的提案中,您必须在概念定义前显式放置auto,以使编译器标志在语法检查后成功。如果未指定auto,则类型必须明确定义概念图以表示它支持该概念。因此,流迭代器永远不会被用来通过 ForwardIterator 检查。
语法重映射
这是另一个特点。模板如
template<InputIterator I>
requires OutputStreamable<I::value_type>
void f(I a, I b) {
while(a != b) std::cout << *a++ << " ";
}
可以像下面这样使用,如果用户提供一个概念图来教编译器如何取消引用整数,从而整数如何满足 InputIterator 概念。
f(1, 10);
这是基于语言的解决方案的好处,我相信 BCCL 永远无法解决。
基于概念的重载
在快速阅读 BCCL 时,我也找不到任何允许这种情况发生的事情。概念匹配失败似乎会导致硬编译错误。被拒绝的提案允许以下内容:
template<ForwardIterator I>
I::difference_type distance(I a, I b) {
I::difference_type d = 0; while(a != b) ++a, ++d;
return d;
}
template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
return b - a;
}
如果一个类型可以与两个模板一起使用,那么将使用第二个模板,因为它更专业:RandomAccessIterator 改进了ForwardIterator 概念。