需要在C++ 20功能模板中定位子句

发布于 2025-01-29 13:11:14 字数 866 浏览 3 评论 0原文

在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()然后是模棱两可的( xpace )。

  • GCC正确,这是两个不同的功能?
  • 如果是这样,两种样式之间的含义是否存在任何技术差异?

编辑:

  • 正如评论中指出的那样,链接的问题指出,函数模板声明和定义必须使用相同的“需要样式”。这种限制的原因是什么?

(我隐约回想起要求“正常化”以决定它们何时等效的概念 - 我想在C ++ 20中不再是这种情况吗?)

In C++20, you can write a constrained function template in a couple of different ways:

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)

According to the accepted answer in this question, these two forms are equivalent (which has always been my understanding).

However, I notice that GCC 12.1 considers (1) and (2) to be two different functions, rather than (2) being a redeclaration: it's possible to provide definitions for both, and attempting to call do_something() is then ambiguous (example).

  • Is GCC correct, and these are two different functions?
  • If so, are there any technical differences in meaning between the two styles?

EDIT:

  • As pointed out in the comments, the linked questions states that a function template declaration and definition must use the same "requires style". What is the reason for this restriction?

(I vaguely recall from the Concepts TS days that requirements underwent "normalisation" to decide when they were equivalent -- I guess this is no longer the case in C++20?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

枕梦 2025-02-05 13:11:14

该领域的措辞有些转移。在C ++ 20中,我们在 [temp。 over.link]/7

  1. 如果两个功能模板在同一范围中声明,具有相同的名称,具有等效的模板头,并且具有返回类型,参数列表和落后需要clauses(如果有),则两个功能模板是等效的同等使用上述规则来比较涉及模板参数的表达式。如果两个函数模板在同一范围中声明,具有相同名称,接受并通过相同的一组模板参数列表来满足,并且具有使用上述规则在功能上等效的返回类型和参数列表,则在功能上等效。比较涉及模板参数的表达式。如果程序的有效性或含义取决于两个构造是否等效,并且在功能上是等效的,但不是等效的,则该程序是不正确的,无需诊断。

  2. [注7:该规则保证等效声明将相互联系,同时不需要实施英雄努力来确保功能等效的声明将被视为独特的。

  //保证相同
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+10&gt;);
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+10&gt;);

//保证不同
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+10&gt;);
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+11&gt;);

//不符合不良的,无需诊断
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+10&gt;);
模板&lt; int i&gt; void f(a&lt; i&gt; a&lt; i+1+2+3+4&gt;);
 

- 结束注释]

在您的示例中:

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

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

这些在功能上是等效的(基本上是相同的约束),但不是等效的(它们具有不同的模板头 - 模板参数之后的要求子句是该模板参数的一部分模板头),这使得无需诊断不需要。在实践中,由于它们不是同等的,因此它们是不同的超载 - 但是由于它们在功能上等效,因此任何试图呼叫的尝试都是模棱两可的。

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


电流措辞,在戴维斯·赫林(Davis Herring href =“ https://eel.is/c++draft/basic.scope.scope.scope#4.3” rel =“ nofollow noreferrer”> [basic..scope.scope]/4 :

两个声明如果(重新)引入相同的名称,均声明构造函数,或者两者都声明驱动器,除非[...]每个声明函数或函数模板,除非[...]两者都声明函数函数模板具有等效的非对象参数类型列表,返回类型(如果有),模板头和尾随需要示例(如果有),如果两者都是非静态成员,则它们具有相应的对象参数。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /p>

这使得两个do_something s 不通电,这使它们成为不同的函数模板。我们不会遇到新的功能等效规则(因此我们不符合诊断,不需要诊断),但是我们只有两个函数模板在所有情况下都必须模棱两可。所以...不是世界上最有用的事情。

The wording in this area has moved around a bit. In C++20, we had this rule in [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]

In your example:

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

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

These are functionally equivalent (they have the same constraints, basically) but not equivalent (they have different template-heads - the requires clause after the template parameters is part of the template-head), which makes this ill-formed no diagnostic required. In practice, because they're not equivalent, they're different overloads - but because they're functionally equivalent, any attempt to call would be ambiguous between them.

As I point out in the other answer, these have the same meaning - it's just that you have to stick with one form for the declaration and the definition if you split them.


The current wording, after Davis Herring's omnibus paper 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.

This makes the two do_somethings not correspond, which makes them different function templates. We don't run into the new functionally equivalent but not equivalent rule (so we're not ill-formed, no diagnostic required), but we just have two function templates that are necessarily ambiguous in all cases. So... not the most useful thing in the world.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文