struct Test {
int field = 30;
Test() { cout << "In ctor" << endl; }
Test(const Test &other) { field = other.field; cout << "In copy ctor" << endl; }
Test(Test &&other) { field = other.field; cout << "In move ctor" << endl; }
Test &operator=(const Test &other) { field = other.field; cout << "In copy assignment" << endl; return *this; }
Test &operator=(Test &&other) { field = other.field; cout << "In move assignment" << endl; return *this; }
~Test() { cout << "In dtor" << endl; }
};
Test get_test() {
Test t;
return t;
}
int main() {
Test t2 = get_test();
}
我认为这是典型的 NRVO 示例。我正在使用 -fno-elide-constructors 进行编译,我看到调用了以下内容:ctor、move ctor、dtor、dtor。
所以第一个ctor调用对应于行Test t;
,move ctor正在main
中构造t2
,然后是临时的从 get_test
返回的内容被销毁,然后 main
中的 t2
被销毁。
我不明白的是:按值返回时不应该有复制构造函数调用吗?也就是说,我认为 get_test
应该制作 t
的拷贝,然后将该拷贝移至 t2
中。看起来 t
立即移至 t2
中。
最佳答案
C++17
从C++17开始,有mandatory copy elison其中说:
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
- In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
(强调我的)
这意味着 t2
是直接从 get_test
返回的 prvalue
构造的。由于使用纯右值
来构造t2
,因此使用了 move 构造函数。请注意,在 C++17 中,标志 -fno-elide-constructors
对返回值优化 (RVO) 没有影响,并且与 NRVO 不同。
C++17 之前版本
但在 C++17 之前,有 non-mandatory copy elison由于您提供了 -fno-elide-constructors 标志,因此 get_test
使用 move 构造函数返回临时 prvalue
。所以你会看到对 move ctor 的第一个调用。然后,该临时文件用于使用 move 构造函数再次初始化 t2,因此我们得到了对 move 构造函数的第二次调用。
关于c++ - 这里不应该有一个复制者调用吗?禁用省略(无命名返回值优化),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72253869/