形式化具有条件要求的概念

Formalizing a concept with conditional requirements

C++20 中概念的一个好处是,此功能允许并鼓励程序员指定(使用 C++ 语言本身)有关其接口的信息,这些信息以前必须在仅供人类使用的文档中,在像英语这样的自然语言。

例如,我有一个泛型算法,我们称它为template<int N, template<int> class X> void foo(X<N>)。我的foo()解决了数值域的某个问题。我关于如何使用 foo() 的纯英文文档是这样说的:“foo() 接受 class X<N> 的参数,foo 的用户必须实现它。class X<N> 有一个整数模板参数 N 描述它有多少行。Class X<N> 提供运算符 [] 来访问行的元素。X<N> 也提供了一个成员函数reduce(),除非N=1,在这种情况下reduction没有意义,因为只有一行。"

我将如何构思这个?我的第一种方法是:

template<class T>
concept Fooable = requires(T x, int i) {
  x[i];
}

但这并没有正式化 reduce() 要求。

如果我只有一个(正式的)概念,那么我不能在 requires 表达式中包含 x.reduce() 因为一些 Fooable classes,即 N=1 的那些,不并且无法实现 reduce() 方法。

我希望我的 requires 表达式包含类似 if constepxr(T::N > 1) x.reduce(); 的内容,但是 if 是控制流语句而不是表达式,因此不能在 requires 表达式中。

问题:我如何使用 C++20 概念将此合约形式化?

好吧,这出奇地简单。

#include <concepts>
#include <cstddef>
#include <type_traits>

template<int N, template<int> class X>
concept Fooable =
requires(X<N> a, int i) { a[i]; } &&
(
    N == 1 ||
    requires(X<N> a) { a.reduce(); }
);

template<int N, template<int> class X>
requires Fooable<N, X>
void foo(X<N>) {}

template<int N>
struct Myx1 {
    int operator[](int) { return 0; };
};

template<int N>
struct Myx2 {
    int operator[](int) { return 0; }
    int reduce() { return 0; }
};

int main() {
    foo(Myx1<1>{});
    foo(Myx1<2>{}); // error - no reduce() and N != 1
    foo(Myx2<2>{});
}

概念中的 || 运算符是 short-circuiting,就像普通运算符一样,因此 N == 1 || something 按预期工作。