C++20:多种类型的概念及其约束,语法正确吗?

C++20: Concepts of multiple types and its constraint, correct syntax?

已确认在即将发布的 c++20 标准中,根据 this reddit report from the recent Cologne ISO C++ Meeting, we will be able to specify a template's concept and for each class/function template, we will be able to set the constraints on its types. However, in documentations and tutorials (e.g. here),我找不到多类型用例的正确语法。


假设我们有一个多类型的概念:

template<typename T1, typename T2>
concept AreEqComparable = requires(T1 a, T2 b) {
    { a == b } -> bool;
};

比方说,我想在两种不同类型之间定义一个简单的比较函数。 我该怎么做? 更具体地说,我应该在下面代码的 ??? 部分写什么:

???
bool are_equal(T1 a, T2 b) { return a == b; }

我在 here, here, and even here 中找不到任何关于此案例的参考资料。我随机尝试了类似的东西:

/* 1 */ template<AreEqComparable T1, T2>
/* 2 */ AreEqComparable<T1, T2>
/* 3 */ template<AreEqComparable<T1, T2>>

但是它们都抛出语法错误。我认为答案应该在规范中的某个地方P0557 by Bjarne Stroustrup,但我快速浏览后无法找到它。

你可以这样写:

template <typename T1, typename T2>
    requires AreEqComparable<T1, T2>
bool are_equal(T1 a, T2 b)
{
    // ...
}

这里我们用一个requires-clause对类型模板参数提出要求

你可以写:

template <typename T1, AreEqComparable<T1> T2>
bool are_equal(T1, T2);

这相当于:

template <typename T1, typename T2>
    requires AreEqComparable<T2, T1>
bool are_equal(T1, T2);

这里的约束中的类型被翻转了,AreEqComparable<T2, T1> 而不是 AreEqComparable<T1, T2>。这对于许多概念来说肯定很重要,但可能不是特别是这个概念,因为 == 本身在 C++20 中变得对称(缺少实际代码中不应该存在的病态情况)。如果你想真的确定这种对称性是有效的,你总是可以在概念中明确说明(因为EqualityComparableWith在工作草案中):

template<typename T1, typename T2>
concept AreEqComparable = requires(T1 a, T2 b) {
    { a == b } -> bool;
    { b == a } -> bool;
};

您实际上可以通过翻转模板参数的顺序以正确的顺序获得您想要的约束 (h/t Matthieu M.):

template <typename T2, AreEqComparable<T2> T1>
bool are_equal(T1, T2);

另一种完全避免引入模板参数的语法(以增加其他冗余为代价):

bool are_equal(auto x,auto y)
  requires AreEqComparable<decltype(x),decltype(y)>
  {return x==y;}

在 GCC 8.2.0 中,概念应该这样写:

concept bool ConceptName = /* ... */

但是C++ Templates: The Complete Guide没有提到bool。由于C++20标准尚未发布,所以很难说哪个是对的。

对于需要一个参数(不一定是类型)的概念,shorthand:

template <UnaryConceptName T>

对于那些需要两个或更多参数的,没有shorthand:

template <typename T1, typename T2> requires BinaryConceptName<T1, T2>

typename可以替换为特定的类型名称。

顺便说一句:

  1. 我上面提到的那本书对概念做了简单的介绍。

  2. 在 GCC 中使用 -fconcepts 来启用概念。