我无法找到以下问题的具体答案:
考虑以下代码:
Obj f() {
Obj o2;
return o2;
}
int main() {
Obj o1 = f();
return 0;
}
复制构造函数在没有编译器优化的情况下被激活了多少次?
如果没有移动构造函数,不是一次将o2复制到调用函数,另一次构造o1吗?
如果有move构造函数,不是一次将o2复制到调用函数,另一次构造o1(第二次是move const)吗?
我无法找到以下问题的具体答案:
考虑以下代码:
Obj f() {
Obj o2;
return o2;
}
int main() {
Obj o1 = f();
return 0;
}
复制构造函数在没有编译器优化的情况下被激活了多少次?
如果没有移动构造函数,不是一次将o2复制到调用函数,另一次构造o1吗?
如果有move构造函数,不是一次将o2复制到调用函数,另一次构造o1(第二次是move const)吗?
Obj
被复制两次。一次通过return
语句(构造返回值),一次o1
通过复制返回值进行初始化。
如果Obj
有一个可用的移动构造函数,它会被移动两次并被复制零次。该return
语句必须使用移动,即使返回的表达式是左值。由于语言中的特殊规则,这种“移动优化”是强制性的;o2
即使禁用了优化,也不得复制。o1
初始化时发生第二次移动。
如果Obj
没有移动构造函数或隐式删除的移动构造函数,则复制构造函数使用两次。
如果Obj
有一个显式删除的移动构造函数,则程序是错误的,因为初始化o1
尝试使用已删除的移动构造函数。
如果Obj
有一个可用的移动构造函数,则在return
执行语句时移动一次。如上所述,编译器必须使用移动而不是复制。的构造o1
既不涉及复制,也不涉及移动。相反,initializes中的return
语句不涉及临时性。这是因为“保证复制省略”:语言要求复制被省略,即使优化被禁用。这是因为是纯右值,并且纯右值不会具体化(即,实例化为临时对象),除非有必要这样做。标准创建的“法律虚构”实际上是返回创建的“配方”f()
o1
f()
f()
Obj
,而不是Obj
本身。在实践中,这可以像在标准的早期版本中实现(可选)返回值优化一样实现:调用者将指针o1
直接传递给 into f
,return
语句构造Obj
到这个指针中。
如果 的移动构造函数Obj
被隐式删除或不存在,则该return
语句将使用复制构造函数,因此将有一个副本和零个移动。
如果显式删除 的移动构造函数Obj
,则程序格式错误,如 C++11/C++14 的情况。
可以优化上述情况下的复制/移动。在涉及多个复制/移动操作的情况下,编译器可以优化其中任何一个或全部。
在 C++17 之前,是的,有两个复制构造函数调用(即使它们有副作用也可以省略)。-fno-elide-constructors
您可以在 gcc/clang中看到这一点。
由于 C++17 由于新的临时实现规则,内部只涉及一个副本f
(同样可以省略)。
准确地说,所有这些都是移动,而不是副本。