这里不应该有一个copy ctor调用吗?禁用省略号(无命名 return 值优化)
Shouldn't there be a copy ctor invocation here? Elision disabled (no named return value optimization)
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;
,移动ctor在main
中构造t2
,然后是从get_test
返回的临时被销毁,则main
中的t2
被销毁。
我不明白的是:按值返回时不应该有一个copy ctor调用吗?也就是说,我认为 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
是直接从 prvalue
和 get_test
return 构造的。由于 prvalue
用于构造 t2
,因此使用了移动构造函数。请注意,在 C++17 中,标志 -fno-elide-constructors
对 return 值优化(RVO) 没有影响,并且与 NRVO 不同。
Pre-C++17
但在 C++17 之前,有 non-mandatory copy elison 并且由于您提供了 -fno-elide-constructors
标志,临时 prvalue
由 return 编辑=12=] 使用移动构造函数。所以你看到了对 move ctor 的第一次调用。然后,该临时文件用于使用移动构造函数再次初始化 t2
,因此我们第二次调用移动构造函数。
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;
,移动ctor在main
中构造t2
,然后是从get_test
返回的临时被销毁,则main
中的t2
被销毁。
我不明白的是:按值返回时不应该有一个copy ctor调用吗?也就是说,我认为 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
是直接从 prvalue
和 get_test
return 构造的。由于 prvalue
用于构造 t2
,因此使用了移动构造函数。请注意,在 C++17 中,标志 -fno-elide-constructors
对 return 值优化(RVO) 没有影响,并且与 NRVO 不同。
Pre-C++17
但在 C++17 之前,有 non-mandatory copy elison 并且由于您提供了 -fno-elide-constructors
标志,临时 prvalue
由 return 编辑=12=] 使用移动构造函数。所以你看到了对 move ctor 的第一次调用。然后,该临时文件用于使用移动构造函数再次初始化 t2
,因此我们第二次调用移动构造函数。