函数返回的局部变量是否在C++20中自动移动?

请考虑一个 C++ 程序,其中函数使用两个构造函数之一从类型的局部变量foo构建其返回U对象int

struct U {
    U(int) {}
    U(int&&) {}
};

U foo(int a = 0) { return a; }

int main() { foo(); }

在 C++17 模式下,程序被所有编译器接受,演示:https : //gcc.godbolt.org/z/b8hhEh948

但是在 C++20 模式下,GCC 拒绝它并显示错误:

In function 'U foo(int)':
<source>:6:27: error: conversion from 'int' to 'U' is ambiguous
    6 | U foo(int a = 0) { return a; }
      |                           ^
<source>:3:5: note: candidate: 'U::U(int&&)'
    3 |     U(int&&) {}
      |     ^
<source>:2:5: note: candidate: 'U::U(int)'
    2 |     U(int) {}
      |     ^

演示:https : //gcc.godbolt.org/z/fMvEPMGhq

我认为这是因为 C++20 特性P1825R0:P0527R1 和 P1155R3 的合并措辞(更多隐式动作)
并且foo根据此特性的函数必须等效于

U foo(int a = 0) { return std::move(a); }

由于所有编译器的构造函数选择歧义而被拒绝,演示:https : //gcc.godbolt.org/z/TjWeP965q

GCC 是 C++20 模式下上述示例中唯一正确的编译器吗?

回答

这里发生的是 gcc 实现 P1825 的方式。

在这个例子中:

U foo(int a) {
    return a;
}

C++17 和 C++20 语言规则(这里没有变化)是我们首先将其a视为右值,如果重载解析失败(在 C++17 中,也需要绑定到int&&),然后我们将a作为左值。有了这条规则,这段代码就可以工作了——a作为 xvalue的重载解析由于歧义而失败(因为U是一种愚蠢的类型),所以我们回退到将其a视为左值,然后成功了。

但是 gcc 的实现并没有这样做。相反,它将axvalue视为也可以绑定到非常量左值引用的 xvalue,并执行单轮重载决议(这样做是为了避免破坏某些代码,请参见此处)。那一轮重载决议是模棱两可的,因为简单地将其a视为 xvalue 是模棱两可的,并且这里没有相关的左值 ref 构造函数,因此 gcc 拒绝了该示例。

但在这种情况下,我会说这是U的错而不是 gcc 的错。如果U(a)像往常一样有两个构造函数采用int const&and int&&,或者 (b) 有一个采用 的构造函数int,则该示例可以正常编译。请注意,在 (b) 情况下,C++17 规则将执行复制,但 C++20 规则将执行移动(因为我们不再要求构造函数专门采用右值引用)。

  • [P2266](http://open-std.org/JTC1/SC22/WG21/docs/papers/2021/p2266r1.html) 应该让 `a` 只是一个 xvalue,所以它是模棱两可的,但不支持一个 `int&amp;` 构造函数。

以上是函数返回的局部变量是否在C++20中自动移动?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>