我的问题源于钻研std::move
在 return
语句,例如在以下示例中:
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你可以阅读
- 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发生
- 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
已经。有效,local
和 a1
一直都是同一个对象。在
A a2(no_nrvo())
, local
有自己的存储,以及函数的结果,又名 a2
是从它移动构造的。有效,local
搬进 a2
.
关于c++ - 按值返回的函数的 return 语句中的初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68883573/