概念和模板约束之间有什么区别?

Ray*_*ery 95 c++ d c++11 c++-concepts

我想知道C++完整概念提议和模板约束之间的语义差异(例如,Dlang中出现的约束或C++ 1y的新概念 - 精简提议).

什么是能够比模板约束做的完整概念不能做到的?

Jos*_*eld 133

以下信息已过期.它需要根据最新的Concepts Lite草案进行更新.

约束提案的第3节以合理的深度涵盖了这一点.

这个概念提议已经被搁置在后面燃烧器上一小段时间,希望能够在更短的时间内充实和实现约束(即概念 - 精简版),目前至少在C++ 14中有针对性.约束提议旨在平滑过渡到后来的概念定义.约束是概念提议的一部分,是其定义中必不可少的构建块.

C++概念库设计中,Sutton和Stroustrup考虑以下关系:

概念=约束+公理

快速总结一下它们的含义:

  1. 约束 - 对类型的静态可评估属性的谓词.纯粹的语法要求.不是域抽象.
  2. 公理 - 假定为真的类型的语义要求.没有静态检查.
  3. 概念 - 算法的一般,抽象要求.根据约束和公理来定义.

因此,如果将公理(语义属性)添加到约束(语法属性),则可以获得概念.


概念 - 精简版

概念 - 精简提案只给我们带来了第一部分,即约束,但这是迈向完全成熟概念的重要而必要的步骤.

约束

约束都是关于语法的.它们为我们提供了一种在编译时静态识别类型属性的方法,以便我们可以根据语法属性限制用作模板参数的类型.在当前的约束提议中,它们使用类似&&和的逻辑连接词用命题演算的子集表达||.

我们来看一下行动中的约束:

template <typename Cont>
  requires Sortable<Cont>()
void sort(Cont& container);
Run Code Online (Sandbox Code Playgroud)

这里我们定义一个名为的函数模板sort.新增加的是要求条款.requires子句为此函数的模板参数提供了一些约束.特别是,此约束表示类型Cont必须是Sortable类型.一个巧妙的事情是它可以用更简洁的形式写成:

template <Sortable Cont>
void sort(Cont& container);
Run Code Online (Sandbox Code Playgroud)

现在,如果您尝试传递任何不考虑Sortable此函数的内容,您将收到一个很好的错误,它会立即告诉您推导出的类型T不是Sortable类型.如果你在C++ 11做到了这一点,你不得不从投入了一些可怕错误里面sort那个没有意义的任何功能.

约束谓词与类型特征非常相似.他们采用一些模板参数类型,并为您提供一些信息.约束试图回答以下关于类型的问题:

  1. 这种类型的运算符是否过载?
  2. 这些类型可以用作此运算符的操作数吗?
  3. 这种类型有这样的特质吗?
  4. 这个常数表达式是否相等?(对于非类型模板参数)
  5. 这种类型是否有一个名为yada-yada的函数返回该类型?
  6. 这种类型是否满足所有语法要求?

但是,约束并不意味着取代类型特征.相反,他们将携手合作.现在可以根据概念和类型特征方面的某些概念来定义某些类型特征.

例子

所以关于约束的重要一点是它们并不关心语义.一些好的约束例子是:

  • Equality_comparable<T>:检查类型是否==包含两个相同类型的操作数.

  • Equality_comparable<T,U>:检查是否存在==给定类型的左右操作数

  • Arithmetic<T>:检查类型是否为算术类型.

  • Floating_point<T>:检查类型是否为浮点类型.

  • Input_iterator<T>:检查类型是否支持输入迭代器必须支持的语法操作.

  • Same<T,U>:检查给定类型是否相同.

您可以使用GCC的特殊概念 - 精简版本来尝试所有这些.


超越Concepts-Lite

现在我们进入概念 - 精简提案之外的一切.这比未来本身更具未来感.从现在开始的一切都可能会发生很大的变化.

公理

公理都是关于语义的.它们指定了关系,不变量,复杂性保证和其他类似的东西.我们来看一个例子.

虽然Equality_comparable<T,U>约束会告诉你,有一个operator== 是接受类型TU,它不会告诉你这是什么操作手段.为此,我们将有公理Equivalence_relation.这个公理说,当这两种类型的物体与operator==给予比较时true,这些物体是等价的.这似乎是多余的,但肯定不是.你可以很容易地定义一个operator==表现得像一个operator<.你这样做是邪恶的,但你可以.

另一个例子是Greater公理.说两个类型的对象T可以><运算符进行比较,这一切都很好,但它们意味着什么?该Greater公理说,当且仅当x是大于y,则y是小于x.建议的规范如此公理如下:

template<typename T>
axiom Greater(T x, T y) {
  (x>y) == (y<x);
}
Run Code Online (Sandbox Code Playgroud)

因此,公理回答了以下类型的问题:

  1. 这两个运营商是否有这种关系?
  2. 这种类型的运算符是否意味着这个?
  3. 这种类型的操作是否具有这种复杂性?
  4. 该运算符的结果是否意味着这是真的?

也就是说,他们完全关注这些类型的类型和操作的语义.这些东西无法静态检查.如果需要检查,类型必须以某种方式宣称它遵守这些语义.

例子

以下是公理的一些常见例子:

  • Equivalence_relation:如果比较两个对象==,它们是等价的.

  • Greater:无论何时x > y,然后y < x.

  • Less_equal:无论何时x <= y,然后!(y < x).

  • Copy_equality:For xyof type T:if x == y,由复制构造创建的同一类型的新对象T{x} == y仍然x == y(即,它是非破坏性的).

概念

现在概念很容易定义; 它们只是约束和公理结合.它们提供了对类型的语法和语义的抽象要求.

例如,请考虑以下Ordered概念:

concept Ordered<Regular T> {
  requires constraint Less<T>;
  requires axiom Strict_total_order<less<T>, T>;
  requires axiom Greater<T>;
  requires axiom Less_equal<T>;
  requires axiom Greater_equal<T>;
}
Run Code Online (Sandbox Code Playgroud)

首先要注意的模板类型TOrdered,它也必须满足的要求Regular概念.该Regular概念是一种非常基本的要求,该类型表现良好 - 可以构建,销毁,复制和比较.

除了这些要求之外Ordered,T满足一个约束和四个公理的要求:

  • 约束:Ordered类型必须有operator<.这是静态检查,因此必须存在.
  • 公理:对于xy类型T:
    • x < y 给出严格的总排序.
    • x大于y,y小于x,反之亦然.
    • x小于或等于y,y不小于x,反之亦然.
    • x大于或等于y,y不大于x,反之亦然.

结合这样的约束和公理可以为您提供概念.它们定义了抽象类型的语法和语义要求,以便与算法一起使用.算法目前必须假设所使用的类型将支持某些操作并表达某些语义.通过概念,我们将能够确保满足要求.

最新的概念设计中,编译器只会检查模板参数是否满足概念的语法要求.公理未加控制.由于公理表示不具有静态可评估性(或通常无法完全检查)的语义,因此类型的作者必须明确声明其类型满足概念的所有要求.这在以前的设计中被称为概念映射,但后来被删除了.

例子

以下是一些概念示例:

  • Regular 类型是可构造的,可破坏的,可复制的,并且可以进行比较.

  • Ordered类型支持operator<,并具有严格的总排序和其他排序语义.

  • Copyable类型是可复制的,可破坏的,如果x等于y并被x复制,则副本也将等于y.

  • Iterator类型必须具有相关的类型value_type,reference,difference_type,和iterator_category它本身必须符合一定的概念.他们还必须支持operator++和解除引用.

概念之路

约束是迈向C++完整概念特性的第一步.它们是非常重要的一步,因为它们提供了类型的静态可执行要求,因此我们可以编写更清晰的模板函数和类.现在我们可以避免一些困难和丑陋std::enable_if以及它的元编程朋友.

但是,约束提案没有做很多事情:

  1. 它不提供概念定义语言.

  2. 约束不是概念图.用户不需要专门注释其类型满足某些约束.它们使用简单的编译时语言功能进行静态检查.

  3. 模板的实现不受其模板参数约束的约束.也就是说,如果您的函数模板对其不应该执行的约束类型的对象执行任何操作,则编译器无法对其进行诊断.一个功能齐全的概念提案可以做到这一点.

约束提案是专门设计的,因此可以在其上引入完整的概念提案.运气好的话,过渡应该是一个相当平稳的过程.概念组正在寻求为C++ 14(或之后的技术报告)引入约束,而完整的概念可能会在C++ 17的某个时候开始出现.

  • *那*是初稿?!* (24认同)
  • 应该注意的是,concepts-lite不会检查对模板本身实现的约束.因此,您可以声称可以使用任何DefaultConstructable,但编译器不会阻止您意外使用复制构造函数.一个功能更全面的概念提案. (5认同)
  • @ScarletAmaranth:因为它归结为在有限有界时间内自动证明一个定理.这有两个障碍:1.证明在有限有界时间内的任何定理在理论上是非常困难的,而且在现有技术中是不可能的.除非证据是基于其他公理,否则你无法证明公理.(在这种情况下,它在数学上变成"不是公理")这些公理旨在告诉编译器"如果你认为它有用,你当然可以反转比较...",或者"是的,交叉点中使用的空集总是给出相同的结果." (4认同)
  • @sftrabbit,非常好的答案.但我有一个问题:编译器如何检查类型是否满足概念的语义要求? (2认同)

Her*_*ter 22

又见最近的第2.3节"什么是'精简版’相关的概念精简版"(3月12日)概念邮电分钟的讨论记录,它被张贴在这里的同一天:http://isocpp.org/blog/2013/03/new-paper-n3576-sg8-concepts-teleconference-minutes-2013-03-12-herb-sutter.