Concepts没有制定C ++ 0x标准,但Boost仍提供The Boost Concept Check Library (BCCL)。我想BCCL并未涵盖C ++ 0x标准的所有内容。 BCCL与提议的C ++ 0x解决方案有什么区别?
答案 0 :(得分:28)
这些手动解决方案的概念差异很大,概念允许对模板的定义进行类型检查而不做任何特殊操作。概念检查库仅允许对其进行 use 进行类型检查。例如:
template<typename InputIterator>
int distance(InputIterator a, InputIterator b)
{ return b - a; }
您现在可以将该模板与概念检查和特征一起使用,但在编写该模板后永远不会出现错误 - 因为标准允许编译器延迟编译模板直到实例化。要进行检查,您必须编写“archetype”类,它们包含接口所需的那些操作,然后人工实例化它们。
阅读BCCL的文档,我发现它已经包含了像“default constructible”这样的常见原型。但是如果你编写自己的概念,你还必须提供自己的原型,这并不容易(你必须找到一个类型必须提供的最小功能)。例如,如果您的原型包含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
概念。
答案 1 :(得分:4)
C ++ 0x概念功能将是核心语言功能,其整个过程将由编译器完成。
Boost概念检查库是几乎相同的功能,但用C ++和宏编写为库来模拟某些功能。它无法完成最终语言功能中所需的所有功能(取决于最终的功能定义),但为模板类型检查(以及其他编译时间检查)提供了一些等效的解决方案。
正如所建议的那样,由于C ++ 0x概念是一种语言特性,它将允许提供更优雅的语义,并允许编译器使用当前不可用于程序的信息,从而在编译时允许更详细或更智能的错误(如概念的第一个目的是允许抽象类型检查模板)。
答案 2 :(得分:3)
免责声明:即使我已经安装了Boost,我也无法在过去30分钟内成功使用BCCL。根据Boost 1.37的BCCL文档,您在下面看到的示例看起来没问题但是没有用。我想这很不利。
使用BCCL,您只能获得类似静态断言的内容,而核心语言概念功能提供完整的模块化类型检查,并且能够阻止某些功能模板参与重载决策。对于本机概念,编译器可以立即检查约束模板的主体,而BCCL不会使编译器在这方面检查任何内容。您必须使用“arche type”参数手动实例化模板,以查看模板是否使用任何不可用的操作(例如,operator - on forward iterators)。
至于重载分辨率,这是一个例子:
template<typename Iter>
void foo(Iter,Iter) {
BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}
void foo(long, int);
int main() {
foo(2,3); // compile-time error
}
模板是更好的匹配,因为非模板需要从int到long的转换。但实例化失败,因为int不是迭代器。你得到一个很好的错误信息解释它,但这不是很令人满意,是吗?使用本机概念,您可以编写
template<typename Iter>
requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
这里,函数模板不会参与重载解析,因为不满足T = int的要求。太好了!
我们仍然可以使用SFINAE技巧来约束函数模板。 C ++ 0x将SFINAE扩展为表达式,并与函数模板的decltype和默认模板参数一起编写
template<typename T> T&& make();
template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
在这种情况下,模板参数推导将无法静默,因为编译器不知道* make&lt; Iter&gt;()应该是什么类型的表达式(我们不能解除引用)一个int)。通过一些模板元编程和一些宏,我们可以非常接近以可读的方式对模板参数施加任意结构约束。以下是对REQUIRES和RandomAccessIterator采用适当定义的看法:
template <typename Iter
REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}
HTH, 小号