c++ - 按值返回的函数的 return 语句中的初始化

标签 c++ stdmove copy-initialization direct-initialization

我的问题源于钻研std::movereturn语句,例如在以下示例中:

struct A
{
    A() { std::cout << "Constructed " << this << std::endl; }
    A(A&&) noexcept { std::cout << "Moved " << this << std::endl; }
 };

A nrvo()
{
    A local;
    return local;
}

A no_nrvo()
{
    A local;
    return std::move(local);
}

int main()
{
    A a1(nrvo());
    A a2(no_nrvo());
}
打印 (MSVC,/std:c++17, release)
Constructed 0000000C0BD4F990
Constructed 0000000C0BD4F991
Moved 0000000C0BD4F992
我对返回按值的函数中的 return 语句的一般初始化规则感兴趣,以及在使用 std::move 返回局部变量时适用哪些规则。如上图所示。
一般情况
关于return statements你可以阅读
  1. Evaluates the expression, terminates the current function and returns the result of the expression to the caller after implicit conversion to the function return type. [...]

在 cppreference.com 上。
其他 Copy initialization发生
  1. when returning from a function that returns by value like so

 return other;
回到我的例子,根据我目前的知识 - 与上述规则相反 - A a1(nrvo());是声明 直接初始化 a1 与纯右值 nrvo() .那么究竟哪个对象是 复制初始化 如 cppreference.com 中所描述的返回语句?
std::move案例
对于这种情况,我引用了 ipc 在 Are returned locals automatically xvalues 上的回答。 .我想确保以下内容是正确的:std::move(local)有类型 A&&但是 no_nrvo()声明返回类型 A ,所以这里

returns the result of the expression to the caller after implicit conversion to the function return type


部分应该发挥作用。我认为这应该是 Lvalue to rvalue conversion :

A glvalue of any non-function, non-array type T can be implicitly converted to a prvalue of the same type. [...] For a class type, this conversion [...] converts the glvalue to a prvalue whose result object is copy-initialized by the glvalue.


转换自 A&&A A的移动构造函数 使用,这也是此处禁用 NRVO 的原因。这些规则是否适用于这种情况,我是否正确理解它们?此外,他们再次说 复制初始化 由泛左值但 A a2(no_nrvo());是直接初始化。所以这也涉及到第一种情况。

最佳答案

在深入了解这些细节时,您必须小心使用 cppreference.com,因为它不是权威来源。

So which object exactly is copy-initialized as described at cppreference.com for return statements?


在这种情况下,没有。这就是复制省略:通常会发生的复制被跳过。 cppreference (4) 子句可以写成“当从按值返回的函数返回时,并且拷贝没有被省略”,但这有点多余。 standard: [stmt.return]在这个问题上要清楚得多。

To convert from A&& to A A's move constructor is used, which is also why NRVO is disabled here. Are those the rules that apply in this case, and did I understand them correctly?


这不太对。 NRVO 仅适用于非 volatile 对象的名称。然而,在 return std::move(local); ,不是local返回的是 A&&这是调用 std::move() 的结果.这没有名称,因此强制性 NRVO 不适用。

I think this should be an Lvalue to rvalue conversion:

A&&返回者 std::move()绝对不是左值。它是一个 xvalue,因此已经是一个右值。这里没有发生左值到右值的转换。

but A a2(no_nrvo()); is a direct initialization. So this also touches on the first case.


并不真地。函数是否必须执行其结果的复制初始化作为 return 的一部分语句不受该函数调用方式的任何影响。同样,函数的返回参数在调用点的使用方式不受函数定义的影响。
在这两种情况下,an由函数的结果直接初始化。实际上,这意味着编译器将为 an 使用相同的内存位置。对象作为函数的返回值。
A a1(nrvo()); ,感谢 NRVO,分配给 local 的内存位置与函数的结果值相同,恰好是 a1已经。有效,locala1一直都是同一个对象。
A a2(no_nrvo()) , local有自己的存储,以及函数的结果,又名 a2是从它移动构造的。有效,local搬进 a2 .

关于c++ - 按值返回的函数的 return 语句中的初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68883573/

相关文章:

c++ - 为什么在 C++17 中复制初始化需要析构函数并保证移动/复制省略?

c++ - ld : duplicate symbol. g++

c++ - 当我执行 int a = std::move(b) (b 也是 int)时,它与 a = b 相同吗?

c++ find_if lambda

C++11: 'decltype class instance declaration' with std::move( ) 不调用 'move constructor.' 为什么?

C++:我应该在 return 语句中明确使用 std::move() 来强制移动吗?

c++ - 在 C++ 中初始化变量的 = 和 {} 语法之间的区别

c++ - POD 变量的直接初始化不起作用,但在将变量插入 vector 时进行复制初始化

c++ - 具有大量相互依赖条件的单元测试代码

c++ - shared_ptr : do I need two or only one 中的常量