概念和模板约束之间有什么区别
What are the differences between concepts and template constraints?
我想知道C++完整概念提案和模板约束之间的语义差异是什么(例如,Dlang中出现的约束或C++1y的新概念lite提案)。
成熟的概念能做什么,而模板约束不能做什么?
以下信息已过期。它需要根据最新的Concepts Lite草案进行更新
约束方案的第3节对此进行了合理深入的介绍。
概念提案已经被搁置了很短一段时间,希望限制(即概念lite)能够在更短的时间内得到充实和实现,目前的目标至少是在C++14中实现一些东西。约束建议旨在作为向稍后概念定义的平稳过渡。约束是概念提案的一部分,也是其定义中必要的构建块。
在C++概念库的设计中,Sutton和Stroustrup考虑了以下关系:
概念=约束+公理
快速总结其含义:
- 约束-一个类型的静态可评估属性上的谓词。纯粹的句法要求。不是领域抽象
- 公理-假设为真的类型的语义要求。未静态检查
- 概念——算法对其论点的一般抽象要求。根据约束和公理进行定义
因此,如果您将公理(语义属性)添加到约束(句法属性)中,您就会得到概念。
概念精简版
概念精简提案只给我们带来了第一部分,即限制,但这是迈向成熟概念的重要和必要的一步。
限制
约束都是关于语法的。它们为我们提供了一种在编译时静态识别类型属性的方法,这样我们就可以根据它们的语法属性来限制用作模板参数的类型。在当前的约束建议中,它们用命题演算的子集来表示,使用逻辑连接词,如&&
和||
。
让我们来看看作用中的一个约束:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
在这里,我们定义了一个名为sort
的函数模板。新添加的是requires子句。requires子句对该函数的模板参数提供了一些约束。特别地,该约束规定类型Cont
必须是Sortable
类型。一件巧妙的事情是,它可以用更简洁的形式写成:
template <Sortable Cont>
void sort(Cont& container);
现在,如果您试图将任何不被视为Sortable
的内容传递给此函数,您将得到一个很好的错误,该错误会立即"告诉"您为T
推导的类型不是Sortable
类型。如果您在C++11中这样做,那么sort
函数内部的就会抛出一些可怕的错误,这对任何人来说都没有意义。
约束谓词与类型特征非常相似。它们采用一些模板参数类型,并向您提供有关它的一些信息。约束试图回答以下类型的问题:
- 这种类型是否重载了某个运算符
- 这些类型可以用作此运算符的操作数吗
- 这种类型有这样那样的特点吗
- 这个常数表达式等于那个吗?(用于非类型模板参数)
- 这个类型是否有一个名为yadayada的函数来返回那个类型
- 这种类型是否满足用作那种类型的所有语法要求
然而,约束并不意味着取代类型特征。相反,他们将携手合作。有些类型特征现在可以用概念来定义,有些概念可以用类型特征来定义。
示例
因此,约束的重要之处在于,它们一点也不关心语义。约束条件的一些好例子是:
Equality_comparable<T>
:检查类型是否具有==
,两个操作数都是同一类型。Equality_comparable<T,U>
:检查是否存在具有给定类型的左右操作数的==
Arithmetic<T>
:检查类型是否为算术类型。Floating_point<T>
:检查类型是否为浮点类型。Input_iterator<T>
:检查类型是否支持输入迭代器必须支持的语法操作。Same<T,U>
:检查给定的类型是否相同。
您可以通过GCC的特殊概念lite构建来尝试所有这些。
超越概念精简版
现在,我们将讨论除概念精简提案之外的所有内容。这甚至比未来本身更具未来感从现在开始,一切都可能发生很大的变化
Axioms
公理都是关于语义的。它们指定了关系、不变量、复杂性保证和其他诸如此类的东西。让我们看一个例子。
虽然Equality_comparable<T,U>
约束会告诉您有一个采用类型T
和U
的operator==
,但它不会告诉您该操作的含义。为此,我们将得到公理Equivalence_relation
。这个公理说,当将这两种类型的对象与给出true
的operator==
进行比较时,这些对象是等价的。这看起来可能是多余的,但肯定不是。您可以很容易地定义一个operator==
,它的行为与operator<
类似。你那样做是邪恶的,但你可以。
另一个例子是Greater
公理。可以说T
类型的两个对象可以与>
和<
运算符进行比较,但它们意味着什么?Greater
公理说,如果x
大于y
,则y
小于x
。这样一个公理的拟议规范看起来像:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
因此公理回答了以下类型的问题:
- 这两个操作符之间有这种关系吗
- 这种类型的运算符是这个意思吗
- 对该类型的操作是否具有这种复杂性
- 这个运算符的结果是否意味着这是真的
也就是说,他们完全关心类型的语义和对这些类型的操作。这些东西不能静态检查。如果需要对此进行检查,则类型必须以某种方式声明它遵守这些语义。
示例
以下是公理的一些常见例子:
Equivalence_relation
:如果两个对象比较==
,则它们是等价的。Greater
:无论何时x > y
,然后y < x
。Less_equal
:无论何时x <= y
,然后!(y < x)
。Copy_equality
:对于T
类型的x
和y
:如果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>;
}
首先要注意的是,要使模板类型T
成为Ordered
,它还必须满足Regular
概念的要求。Regular
概念是一个非常基本的要求,即类型表现良好——它可以被构造、销毁、复制和比较。
除了这些要求之外,Ordered
还要求T
满足一个约束和四个公理:
- 约束:
Ordered
类型必须具有operator<
。这是静态检查的,因此它必须存在 - 公理:对于
T
类型的x
和y
: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
及其元编程朋友的一些困难和丑陋。
然而,有许多事情是限制提案没有做到的:
它不提供概念定义语言。
约束不是概念图。用户不需要将其类型专门注释为满足某些约束。它们使用简单的编译时语言特性进行静态检查。
模板的实现不受其模板参数上的约束的约束。也就是说,如果函数模板对受约束类型的对象执行了它不应该执行的任何操作,编译器就无法诊断出这一点。一个功能齐全的概念提案将能够做到这一点。
约束方案经过专门设计,可以在其上引入完整的概念方案。如果幸运的话,过渡应该相当顺利。概念小组正在寻求为C++14引入约束(或在不久后的技术报告中),而完整的概念可能会在C++17前后开始出现。
另请参阅最近(3月12日)concepts telecoon会议记录和讨论记录的第2.3节中的"关于concepts lite的lite是什么",该会议记录于同一天发布在此处:http://isocpp.org/blog/2013/03/new-paper-n3576-sg8-concepts-teleconference-minutes-2013-03-12-herb-sutter。
我的2美分:
-
概念精简版提案并不意味着对模板实现进行"类型检查"。即,Concepts-lite将确保(名义上)模板实例化站点的接口兼容性。引用该论文:"concepts lite是C++的一个扩展,它允许使用谓词来约束模板参数"。就是这样。它并没有说模板主体将根据谓词进行检查(隔离)。这可能意味着当您谈论lite概念时,不存在archtypes的一流概念。如果我没记错的话,在概念中,archtype是指提供不少于也不多于的类型,以满足模板的实现。
-
concepts-lite使用了编译器支持的带有语法技巧的美化constexpr函数。查找规则没有更改。
-
程序员不需要编写概念图。
-
最后,再次引用"约束建议没有直接涉及语义的指定或使用;它只针对检查语法。"这意味着公理不在范围内(到目前为止)。
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 这 4 个 lambda 表达式之间有什么区别?
- 空指针常量 (nullptr)、空指针值和空成员指针值之间有什么区别?
- 不同的类或结构初始化方法之间的性能差异是什么?
- "constinit"和"constexpr"之间的真正区别是什么?
- 在什么条件下使用 std::memcpy 在对象之间复制是安全的?
- 移动语义和深层/浅层复制之间有什么关系?
- 这两种C++语法之间有什么区别?
- #include < conio.h> 和 getch() 方法之间的关系是什么?
- ((int) a) 和 (int(a)) 之间的区别是什么?
- ( var > x) 和 ( x < var)之间有什么区别吗?
- 在 C 和 C++ 中作为函数参数,int **a 和 int a[][] 之间有什么确切的区别
- 返回引用实例和非引用实例(return mystr & vs mystr)之间的区别是什么?
- 超市管理系统的类别之间应该是什么关系?
- 指针和程序性能之间有什么关系吗?
- 这些初始化之间有什么区别?
- 空字符和"\0"之间有什么区别?
- 无符号长整型和无符号 int 之间有什么区别,这 2 种类型应该如何在 c# 中封送?