用条件要求形式化概念

cs-*_*cs- 5 c++ c++-concepts c++20

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

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

我该如何理解这一点?我的第一个方法是:

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

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

如果我只有一个(形式)概念,那么我不能在 require 表达式中包含 x.reduce() ,因为某些 Fooable 类(即那些 N=1 的类)没有也不能实现 reduce() 方法。

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

问题:如何使用 C++20 概念来形式化此契约?

Kam*_*Cuk 4

嗯,这出奇地简单。

#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>{});
}
Run Code Online (Sandbox Code Playgroud)

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