【问题标题】:C++ copy constructor activation in return-by-vlaue在按值返回中激活 C++ 复制构造函数
【发布时间】:2021-01-30 20:39:22
【问题描述】:

我无法找到以下问题的具体答案:

考虑以下代码:

Obj f() {
    Obj o2;
    return o2;
}

int main() {
    Obj o1 = f();
    return 0;
}

复制构造函数被激活了多少次没有编译器优化

万一没有移动构造函数,是不是一次将o2复制到调用函数,另一次构造o1?

如果有 move 构造函数,是不是一次将o2复制到调用函数,另一次构造o1(第二次是move const)?

【问题讨论】:

  • 你能说明Obj的定义吗?
  • 这取决于您使用的 C++ 版本。
  • @Brian 这有什么关系?
  • @FrançoisAndrieux 是的,我知道,这不是问题的重点,所以我只是想尽可能少写代码。
  • @YanivK。这很重要,因为我们需要查看Obj 是否有移动构造函数。

标签: c++ copy-constructor return-by-value


【解决方案1】:

C++03 及之前

Obj 被复制两次。一次通过return 语句(构造返回值),一次通过复制返回值来初始化o1

C++11 和 C++14

如果Obj 有一个可用的移动构造函数,它会被移动两次并复制零次。 return 语句必须使用移动,即使返回的表达式是左值。由于语言中的特殊规则,这种“移动优化”是强制性的; o2 不得复制,即使禁用了优化也是如此。初始化o1 时会发生第二次移动。

如果Obj 没有移动构造函数或隐式删除的移动构造函数,则复制构造函数被使用两次。

如果Obj 有一个显式删除的移动构造函数,则程序是病态的,因为o1 的初始化尝试使用已删除的移动构造函数。

C++17 及更高版本

如果Obj 有一个可用的移动构造函数,它会在return 语句执行时被移动一次。如上所述,编译器必须使用移动而不是复制。 o1 的构造既不涉及复制也不涉及移动。相反,f() 中的 return 语句会初始化 o1,而无需临时参与。这是因为“保证复制省略”:语言要求复制被省略,即使优化被禁用。这是因为f() 是纯右值,并且纯右值不会具体化(实例化为临时对象),除非有必要这样做。该标准创建的“法律虚构”是f() 实际上返回了创建Obj 的“配方”,而不是Obj 本身。在实践中,这可以像在标准的早期版本中实现(可选)返回值优化一样实现:调用者将指向o1 的指针直接传递给f,并且return 语句构造@ 987654344@这个指针。

如果Obj的移动构造函数被隐式删除或不存在,则复制构造函数将被return语句使用,所以一复制零移动。

如果 Obj 的移动构造函数被显式删除,则程序是不正确的,如 C++11/C++14 的情况。

在所有情况下

上述情况下的复制/移动可以优化出来。在涉及多个复制/移动操作的情况下,编译器可以优化其中任何一个或全部。

【讨论】:

    【解决方案2】:

    在 C++17 之前,是的,有两个复制构造函数调用(即使它们有副作用也可以省略)。您可以在 gcc/clang 中使用 -fno-elide-constructors 看到这一点。

    从 C++17 开始,由于新的临时实现规则,f 中只涉及一个副本(同样可以省略)。


    准确地说,所有这些都是移动,而不是复制。

    【讨论】:

      猜你喜欢
      • 2021-03-23
      • 1970-01-01
      • 2019-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多