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


96

我想知道C ++完整概念提议和模板约束(例如,Dlang中出现的约束或C ++ 1y的新的 Lites -lite提议)之间的语义区别是什么。

模板约束不能完成的成熟概念有什么功能?


2
约束提议涉及到这一点。
克里斯

我回想起4.8设计概念,但是实际上没有更多关于概念如何进一步约束的方法列出。如果有的话,在对提案的约束条件有了更好的理解之后,Google搜索的概念现在可能会发现一些容易发现的差异。
克里斯,

IMHO的概念提高了可读性,并提供了亚历山大·斯蒂芬诺夫(Alexander Stepanov)等人在很久以前的“编程元素”中所要求的更清晰的编程能力。精简版提案只是朝此方向迈进,以减轻当前所需的怪异的enable_if类型约束的负担。通用编程越早越好。
迪尔维尼

Answers:


135

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

约束建议的第3节在合理的深度中对此进行了介绍。

概念提案已经搁浅了片刻,希望可以充实并在更短的时间范围内实现约束(即,lites-lite),目前至少针对C ++ 14。约束提议旨在充当向概念的更高定义的平稳过渡。约束是概念建议的一部分,并且是其定义中的必要组成部分。

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

概念=约束+公理

为了快速总结其含义:

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

因此,如果将公理(语义属性)添加到约束(句法属性),则会得到概念。


精简版

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

约束条件

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

让我们看一下约束的作用:

template <typename Cont>
  requires Sortable<Cont>()
void sort(Cont& container);

在这里,我们定义了一个名为的功能模板sort。新增加的是require子句。require子句对该函数的模板参数给出了一些约束。特别是,此约束表明类型Cont必须是Sortable类型。一件整洁的事情是,它可以用更简洁的形式写成:

template <Sortable Cont>
void sort(Cont& container);

现在,如果您尝试将不考虑的任何内容传递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的一个特殊的Lite版本来尝试所有这些。


超越概念精简版

现在,我们进入了除了轻量级概念提案之外的所有内容。这比未来本身更具未来性。从这里到现在的一切都可能会发生很大变化。

公理

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

尽管Equality_comparable<T,U>约束会告诉你有一个operator== 需要类型T和的约束U,但是它并没有告诉你该操作的含义。为此,我们将拥有公理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);
}

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

  1. 这两个操作员之间是否存在这种关系?
  2. 某某类型的此运算符表示这个吗?
  3. 这种类型的操作是否具有这种复杂性?
  4. 该运算符的结果是否暗示这是真的?

也就是说,他们完全关心类型的语义以及对这些类型的操作。这些东西不能被静态检查。如果需要对此进行检查,则类型必须以某种方式声明其遵循这些语义。

例子

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

  • Equivalence_relation:如果两个对象比较==,则它们是等效的。

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

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

  • Copy_equality:对于xy类型T:如果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>;
}

首先请注意,要使模板类型TOrdered,它还必须满足Regular概念的要求。该Regular概念是类型良好的非常基本的要求-可以对其进行构造,销毁,复制和比较。

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

  • 约束:Ordered类型必须具有operator<。这是静态检查的,因此它必须存在。
  • 公理:对于xy类型T
    • x < y 给出严格的整体订购。
    • x大于时yy小于x,反之亦然。
    • x小于或等于时yy不小于x,反之亦然。
    • x大于或等于时yy不大于x,反之亦然。

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

最新的概念设计中,编译器将仅检查模板参数是否满足概念的句法要求。公理未经检查。由于公理表示无法静态评估的语义(或通常无法完全检查的语义),因此类型的作者必须明确声明其类型符合概念的所有要求。在以前的设计中,这被称为概念映射,但是此后已被删除。

例子

以下是一些概念示例:

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

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

  • Copyable类型是可复制构造的,可破坏构造的,并且如果x等于yx被复制,则副本还将比较等于y

  • Iterator类型必须具有相关的类型value_typereferencedifference_type,和iterator_category它本身必须符合一定的概念。它们还必须支持operator++并且可以取消引用。

通往概念之路

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

但是,约束提议在很多方面没有做:

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

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

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

约束提议是专门设计的,因此可以在其之上引入完整的概念提议。运气好的话,这种过渡应该是相当顺利的。概念小组正在寻求引入C ++ 14的约束(或随后不久的技术报告中),而完整的概念可能会在C ++ 17左右的某个时候出现。


5
应当注意,concept-lite不会根据模板本身的实现检查约束。因此,您可以声称可以使用任何DefaultConstructable,但是编译器不会偶然阻止您使用复制构造函数。一个功能更全的概念提案将会。
Nicol Bolas 2013年

24
初稿吗?
Nicol Bolas 2013年

2
@sftrabbit,很好的答案。但是我有一个问题:编译器将如何检查类型是否满足概念的语义要求?
Rayniery 2013年

1
是否会在运行时检查(有很多不确定性)“轴心”,或者它们仅用作一种Promise标签?
Red XIII

4
@ScarletAmaranth:因为它归结为在有限的有限时间内自动证明一个定理。这有两个障碍:1.在有限的有限时间内证明任何定理,这在理论上是极其困难的,而当前的技术是不可能的。2.除非证明基于其他公理,否则您无法证明公理。(在这种情况下,数学上将成为“非公理”)这些公理旨在告诉编译器“如果您认为有用,当然可以反转比较...”或“是的,始终在交集中使用EmptySet给出相同的结果。”
Laurent LA RIZZA 2013年


4

我的2美分:

  1. Lite-Lite建议并不意味着对模板实现进行“类型检查” 。即,Concepts-lite将确保(在名义上)模板实例化站点的接口兼容性。引用本文的内容:“ concepts lite是C ++的扩展,允许使用谓词来约束模板参数”。就是这样。它并没有说将针对谓词检查模板主体(单独)。这可能意味着当您谈论轻度概念时,没有一流的原型概念。如果我没记错的话,在概念繁杂的提案中,原型是提供不多也不再满足实现的类型模板而。

  2. 精简版Concepts使用美化的constexpr函数以及编译器支持的一些语法技巧。查找规则没有变化。

  3. 不需要程序员编写概念图。

  4. 最后,再次引用“约束建议不直接解决语义的指定或使用;它仅针对检查语法。” 这将意味着公理不在范围内(到目前为止)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.