微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++ 复制构造函数在 return-by-vlaue 中激活 C++03 及之前C++11 和 C++14C++17 及更高版本在所有情况下

如何解决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 举报,一经查实,本站将立刻删除。