非模板函数尾随要求子句的编译器差异

Compiler variance for trailing requires-clauses on non-templated functions

考虑以下示例:

void f() requires true { }
int main() { f(); }

Clang(1) (DEMO) accepts this program, whereas GCC(1) (DEMO) 拒绝它并出现以下错误:

error: constraints on a non-templated function

请注意,约束表达式实际上可以用于 Clang 的情况,因为以下程序被 Clang 拒绝:

void f() requires false { }
int main() { f(); }  //  // error: no matching function for call to 'f'

with Clang 注意到声明的 f 不是候选者,因为不满足约束(因为它是 f() 调用的候选者;DEMO)。


(1) GCC HEAD 11.0.0 20210124 和 Clang HEAD 12.0.0 (20210124),-std=c++20.

除非另有说明,否则以下所有标准参考文献均指 N4861 (March 2020 post-Prague working draft/C++20 DIS)


这是一个 Clang 错误,GCC 拒绝该程序是正确的,根据 [dcl.decl]/4 (as well as [temp.constr.decl]/1) [强调 我的]:

The optional requires-clause in an init-declarator or member-declarator shall be present only if the declarator declares a templated function ([dcl.fct]). When present after a declarator, the requires-clause is called the trailing requires-clause. [...]

同一段落还包含一个(非规范的)示例,明确指出 OP 的示例格式不正确:

[ Example:

void f1(int a) requires true;       // error: non-templated function

template<typename T>
auto f2(T a) -> bool requires true; // OK

// ...

end example ]

我们可能会注意到,在(成为 C++20 的)工作草案的早期版本中,N4810、[dcl.decl]/4 对 where requires-clauses 的要求较弱被允许出现:

The optional requires-clause (Clause 13) in an init-declarator or member-declarator shall not be present when the declarator does not declare a function (9.2.3.5). [...]

[ Example:

void f1(int a) requires true; // OK

// ...

end example ]

其中 OP 用例的非规范示例明确显示为格式正确。最初的意图可以说是允许根据 class 模板的模板参数来约束 class 模板的非模板成员函数:

#include <iostream>

template<bool B>
struct S {
    void f() requires   B  { std::cout << "true\n"; }
    void f() requires (!B) { std::cout << "false\n"; }
};

int main() { 
    S<true>{}.f();   // true
    S<false>{}.f();  // false
} 

N4861 中 [dcl.decl]/4 的最终状态(针对 C++20)仍然允许这样做,其中限制是 模板函数(参见[temp.pre]/8, particularly [temp.pre]/8.3中的templated entity),它不仅仅涵盖函数模板(以及非模板和模板的函数模板成员class模板),还有 class 个模板的非模板成员函数。


Clang 错误报告: