需要 C++20 函数模板中的子句定位

Requires clause positioning in C++20 function templates

在 C++20 中,您可以通过几种不同的方式编写约束函数模板:

template <typename T>
concept Fooable = true;

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

根据中接受的答案,这两种形式是等价的(我一直是这样理解的)。

但是,我注意到 GCC 12.1 认为 (1) 和 (2) 是两个不同的函数,而不是 (2) 是重新声明:可以为两者提供定义,并尝试调用 do_something() 则不明确 (example).

编辑:

(我依稀记得在 Concepts TS 时代,需求经历了“规范化”以决定它们何时等效——我猜这在 C++20 中不再是这种情况?)

这方面的措辞有所变动。在 C++20 中,我们在 [temp.over.link]/7:

中有这条规则
  1. Two function templates are equivalent if they are declared in the same scope, have the same name, have equivalent template-heads, and have return types, parameter lists, and trailing requires-clauses (if any) that are equivalent using the rules described above to compare expressions involving template parameters. Two function templates are functionally equivalent if they are declared in the same scope, have the same name, accept and are satisfied by the same set of template argument lists, and have return types and parameter lists that are functionally equivalent using the rules described above to compare expressions involving template parameters. If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required.

  2. [Note 7: This rule guarantees that equivalent declarations will be linked with one another, while not requiring implementations to use heroic efforts to guarantee that functionally equivalent declarations will be treated as distinct.

// guaranteed to be the same
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);

// guaranteed to be different
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);

// ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);

-end note]

在你的例子中:

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

它们在功能上是等效的(它们基本上具有相同的约束)但不等效(它们具有不同的 template-heads - 模板参数之后的 requires 子句是 template-head 的一部分),其中使这个 ill-formed 不需要诊断。在实践中,因为它们不是 等效的 ,它们是不同的重载 - 但因为它们在功能上是等效的,所以它们之间的任何调用尝试都是不明确的。

正如我在另一个答案中指出的那样,它们具有相同的含义 - 只是如果将它们分开,您必须坚持使用一种形式的声明和定义。


当前 措辞,根据 Davis Herring 的综合论文 P1787, involves going up to [basic.scope.scope]/4:

Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless [...] each declares a function or function template, except when [...] both declare function templates with equivalent non-object-parameter-type-lists, return types (if any), template-heads, and trailing requires-clauses (if any), and, if both are non-static members, they have corresponding object parameters.

这使得两个do_something不对应,这使得它们成为不同的功能模板。我们没有 运行 进入新的功能等效但不等效的规则(所以我们不是 ill-formed,不需要诊断),但我们只有两个函数模板,它们在所有情况下都必然是模棱两可的。所以...不是世界上最有用的东西。