如何解决C++ 复制构造函数在 return-by-vlaue 中激活 C++03 及之前C++11 和 C++14C++17 及更高版本在所有情况下
我无法找到以下问题的具体答案:
考虑以下代码:
Obj f() {
Obj o2;
return o2;
}
int main() {
Obj o1 = f();
return 0;
}
复制构造函数被激活了多少次没有编译器优化?
如果没有移动构造函数,是不是一次将 o2 复制到调用者函数,另一次是构造 o1?
如果有移动构造函数,是不是一次将o2复制到调用者函数,另一次是构造o1(第二次是move const)?
解决方法
C++03 及之前
Obj
被复制两次。一次通过 return
语句(构造返回值),一次通过复制返回值来初始化 o1
。
C++11 和 C++14
如果 Obj
有一个可用的移动构造函数,它被移动两次并复制零次。 return
语句必须使用移动,即使返回的表达式是左值。由于语言中的特殊规则,这种“移动优化”是强制性的;不得复制 o2
,即使禁用了优化。初始化 o1
时发生第二次移动。
如果 Obj
没有移动构造函数或隐式删除的移动构造函数,则复制构造函数被使用两次。
如果 Obj
具有显式删除的移动构造函数,则程序格式错误,因为 o1
的初始化尝试使用已删除的移动构造函数。
C++17 及更高版本
如果 Obj
有一个可用的移动构造函数,它会在执行 return
语句时移动一次。如上所述,编译器必须使用移动而不是复制。 o1
的构造既不涉及复制也不涉及移动。相反,return
中的 f()
语句会初始化 o1
,而不涉及临时变量。这是因为“保证复制省略”:即使优化被禁用,语言也要求复制被省略。这是因为 f()
是一个纯右值,并且一个纯右值不会被物化(i.e., 实例化为一个临时对象),除非有必要这样做。标准创建的“法律虚构”是 f()
实际上返回创建 Obj
的“配方”,而不是 Obj
本身。在实践中,这可以与在标准的早期版本中实现(可选)返回值优化相同的方式实现:调用者将指向 o1
的指针直接传递给 f
,并且 {{ 1}} 语句将 return
构造到此指针中。
如果Obj
的移动构造函数被隐式删除或不存在,Obj
语句将使用复制构造函数,因此会出现一次复制和零次移动。
如果 return
的移动构造函数被显式删除,则程序与 C++11/C++14 的情况一样格式错误。
在所有情况下
上述情况下的副本/移动可以优化。在涉及多个复制/移动操作的情况下,编译器可以优化其中的任何一个或全部。
,在 C++17 之前,是的,有两个复制构造函数调用(即使它们有副作用,也可以省略)。您可以在 gcc/clang 中使用 -fno-elide-constructors
看到这一点。
从 C++17 开始,由于新的临时物化规则,f
中只涉及一个副本(同样可以省略)。
准确地说,所有这些都是移动,而不是复制。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。