class 模板的 requires 子句是否必须在成员定义之外重复?

Does a class template's requires clause have to be repeated outside member definitions?

当使用 requires 子句的 class 模板成员定义在 class 之外时,如果 requires 是未指定,而 clang 指定。

考虑下面的代码片段:

#include <concepts>

template<typename Container>
    requires std::integral<typename Container::value_type>
class Foo {
public:
    void func();
};

template<typename Container>
void Foo<Container>::func()
{}

编译使用gcc不报错

clang报如下错误:

❯ clang++ -std=c++2a test.cpp
test.cpp:10:1: error: requires clause differs in template redeclaration
template<typename Container>
^
test.cpp:4:19: note: previous template declaration is here
    requires std::integral<typename Container::value_type>
                  ^
1 error generated.

如果我改变定义如下:

template<typename Container>
    requires std::integral<typename Container::value_type>
void Foo<Container>::func()
{}

现在clang不抱怨了

来自gcc --version的输出:

gcc (GCC) 10.2.0

来自clang --version的输出:

clang version 10.0.1 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

这是要报告的错误吗?

来自[temp.mem.func]/1[摘录,强调我的]:

A member function of a class template may be defined outside of the class template definition in which it is declared. [Example:

A constrained member function can be defined out of line:

template<typename T> concept C = requires {
  typename T::type;
};

template<typename T> struct S {
  void f() requires C<T>;
  void g() requires C<T>;
};

template<typename T>
  void S<T>::f() requires C<T> { }  // OK
template<typename T>
  void S<T>::g() { }                // error: no matching function in S<T>

end example]

特别注意 (non-normative) 文本的最后一个示例。

因此,Clang 拒绝是正确的,而 GCC 接受第一个程序作为 out-of-line 定义是错误的

template<typename Container>
void Foo<Container>::func() {}

不匹配Foo<Container>中的任何函数。

(我还没有找到一个公开的 GCC 错误报告)

应该为 GCC 提交错误,因为它接受代码,即使 class 之外的成员声明没有等效的 template-head。

[temp.class]

3 When a member function, a member class, a member enumeration, a static data member or a member template of a class template is defined outside of the class template definition, the member definition is defined as a template definition in which the template-head is equivalent to that of the class template ([temp.over.link]).

[temp.over.link]

6 Two template-heads are equivalent if their template-parameter-lists have the same length, corresponding template-parameters are equivalent and are both declared with type-constraints that are equivalent if either template-parameter is declared with a type-constraint, and if either template-head has a requires-clause, they both have requires-clauses and the corresponding constraint-expressions are equivalent.

templates-heads 的等价要求两者都有一个等价的 requires 子句。省略它完全破坏了等价性。