C++0x 概念 — 历史常见问题解答
C++0x “概念” 发生了什么?
它们对于 C++0x/C++11 来说过于复杂,因此从该标准中移除,以便继续对其进行简化以用于未来的标准。这项工作一直在进行中,一个经过彻底简化的版本“Concepts Lite”将作为技术规范成为 C++14 交付物的一部分。
“概念”是一种旨在精确规范模板参数要求的功能。不幸的是,委员会决定进一步研究概念可能会严重延迟 C++0x/C++11 标准,并投票将该功能从工作草案中移除。有关解释,请参阅 Stroustrup 的笔记《C++0x “移除概念” 决定》和《DevX 对 Stroustrup 关于概念及其对 C++0x 影响的采访》。
本常见问题解答中保留了 C++0x 时代的“概念”部分,作为历史信息。
- C++0x 概念
- C++0x 公理 (语义假设)
- C++0x 概念映射
什么是 C++0x 概念?
注意:这是一个历史部分。“C++0x 概念”并未进入 C++11,正在进行彻底的重新设计。
“概念”是一种描述类型、类型组合以及类型和整数组合要求的机制。它对于尽早检查模板的使用特别有用。反过来,它也有助于尽早发现模板主体中的错误。考虑标准库算法 fill
template<ForwardIterator Iter, class V> // types of types
requires Assignable<Iter::value_type,V> // relationships among argument types
void fill(Iter first, Iter last, const V& v); // just a declaration, not definition
fill(0, 9, 9.9); // Iter is int; error: int is not a ForwardIterator
// int does not have a prefix *
fill(&v[0], &v[9], 9.9); // Iter is int; ok: int* is a ForwardIterator
请注意,我们只声明了 fill()
;我们没有定义它(提供它的实现)。另一方面,我们明确说明了 fill()
对其参数的要求
- 参数
first
和last
必须是ForwardIterator
类型(并且它们必须是相同类型)。 - 第三个参数
v
必须是可赋值给ForwardIterator
的value_type
的类型。
当然,我们读过标准,所以我们知道这一点。然而,编译器不读取需求文档,所以我们必须在代码中使用 ForwardIterator
和 Assignable
概念来告诉它。结果是,fill()
使用中的错误会立即在使用点捕获,并且错误消息也得到了极大改进。编译器现在拥有关于程序员意图的信息,以便进行良好的检查和诊断。
概念也有助于模板实现者。考虑
template<ForwardIterator Iter, class V>
requires Assignable<Iter::value_type,V>
void fill(Iter first, Iter last, const V& v)
{
while (first!=last) {
*first = v;
first=first+1; // error: + not defined for Forward_iterator
// (use ++first)
}
}
此错误会立即捕获,从而消除了大量繁琐的测试(当然并非所有测试)。
能够对不同类型的类型进行分类和区分,我们可以根据传入的类型进行重载。例如
// iterator-based standard sort (with concepts):
template<Random_access_iterator Iter>
requires Comparable<Iter::value_type>
void sort(Iter first, Iter last); // use the usual implementation
// container-based sort:
template<Container Cont>
requires Comparable<Cont::value_type>
void sort(Cont& c)
{
sort(c.begin(),c.end()); // simply call the iterator version
}
void f(vector<int>& v)
{
sort(v.begin(), v.end()); // one way
sort(v); // another way
// ...
}
您可以定义自己的概念,但首先,标准库提供了各种有用的概念,例如 ForwardIterator
、Callable
、LessThanComparable
和 Regular
。
注意:C++0x 标准库是使用概念指定的。
另请参见
- C++ 草案 14.10 概念
- [N2617=08-0127] Douglas Gregor、Bjarne Stroustrup、James Widman 和 Jeremy Siek:《概念的拟议措辞(修订版 5)》(最终提案)。
- Douglas Gregor、Jaakko Jarvi、Jeremy Siek、Bjarne Stroustrup、Gabriel Dos Reis 和 Andrew Lumsdaine:《概念:C++ 中泛型编程的语言支持》。OOPSLA’06,2006 年 10 月。
什么是 C++0x 概念映射?
注意:这是一个历史部分。“C++0x 概念”并未进入 C++11,正在进行彻底的重新设计。
一个 int*
是一个 ForwardIterator
;我们在介绍概念时说过,标准一直都是这么说的,甚至 STL 的第一个版本也使用指针作为迭代器。然而,我们也谈到了 ForwardIterator
的 value_type
。但是 int*
没有名为 value_type
的成员;事实上,它没有成员。那么 int*
怎么会是 ForwardIterator
呢?这是因为我们说它是。使用 concept_map
,我们说当 T*
在需要 ForwardIterator
的地方使用时,我们将其 T
视为其 value_type
template<Value_type T>
concept_map ForwardIterator<T*> { // T*'s value_type is T
typedef T value_type;
};
concept_map
允许我们说明我们希望如何看待一种类型,从而省去了修改它或将其包装成新类型的麻烦。“概念映射”是一种非常灵活和通用的机制,用于使独立开发的软件适应通用用途。
另请参见
- C++ 草案 14.10.2 概念映射
- [N2617=08-0127] Douglas Gregor、Bjarne Stroustrup、James Widman 和 Jeremy Siek:《概念的拟议措辞(修订版 5)》(最终提案)。
- Douglas Gregor、Jaakko Jarvi、Jeremy Siek、Bjarne Stroustrup、Gabriel Dos Reis 和 Andrew Lumsdaine:《概念:C++ 中泛型编程的语言支持》。OOPSLA’06,2006 年 10 月。
什么是 C++0x 公理?
注意:这是一个历史部分。“C++0x 概念”并未进入 C++11,正在进行彻底的重新设计。
axiom
是一组谓词,用于指定概念的语义。公理的主要用例是外部工具(例如,不是常见的编译器操作),例如用于领域特定优化的工具(用于指定程序转换的语言是公理动机的重要组成部分)。次要用途只是在标准中精确地指定语义(如在标准库规范的许多部分中使用)。公理对于某些优化(由编译器和传统优化器完成)也可能有用,但编译器不需要注意用户提供的公理;它们根据标准定义的语义工作。
axiom
列出了可以被认为是等效的计算对。考虑
concept Semigroup<typename Op, typename T> : CopyConstructible<T> {
T operator()(Op, T, T);
axiom Associativity(Op op, T x, T y, T z) {
op(x, op(y, z)) <=> op(op(x, y), z); // T's operator may be assumed to be associative
}
}
concept Monoid<typename Op, typename T> : Semigroup<Op, T> { // a monoid is a semigroup with an identity element
T identity_element(Op);
axiom Identity(Op op, T x) {
op(x, identity_element(op)) <=> x;
op(identity_element(op), x) <=> x;
}
}
<=>
是等效运算符,仅在公理中使用。请注意,您不能(通常)证明公理;我们使用公理来陈述我们无法证明但程序员可以陈述为可接受的假设的内容。请注意,对于某些值,等效语句的两边可能都是非法的,例如对浮点类型使用 NaN(非数字):如果等效的两边都使用 NaN,则两者(显然)都无效且等效(与公理所说的无关),但如果只有一边使用 NaN,则可能有机会利用公理。
公理是一系列等效语句(使用 <=>
)和条件语句(形式为“if (something)
则我们可以假设以下等效”)
// in concept TotalOrder:
axiom Transitivity(Op op, T x, T y, T z)
{
if (op(x, y) && op(y, z)) op(x, z) <=> true; // conditional equivalence
}
另请参见
- C++ 草案 14.9.1.4 公理