【问题标题】:When is the compiler allowed to optimize out the copy-constructor [duplicate]何时允许编译器优化复制构造函数 [重复]
【发布时间】:2019-02-26 00:32:30
【问题描述】:

今天我遇到了一些我不太了解的关于复制构造函数的事情。

考虑下一个代码:

#include <iostream>
using namespace std;

class some_class {
  public:
    some_class() {

    }

    some_class(const some_class&) {
      cout << "copy!" << endl;
    }

    some_class call() {
      cout << "is called" << endl;
      return *this;  // <-- should call the copy constructor
    }
};

some_class create() {
  return some_class();
}

static some_class origin;
static some_class copy = origin; // <-- should call the copy constructor

int main(void)
{
  return 0;
}

然后在将原点分配给副本时调用复制构造函数,这是有道理的。但是,如果我将副本声明更改为

static some_class copy = some_class();

它没有被调用。即使我使用create() 函数,它也不会调用复制构造函数。 但是,当将其更改为

static some_class copy = some_class().call();

它确实调用了复制构造函数。 一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,否则它可能会或可能不会做一些明显的事情,对吧? 那么什么时候允许编译器优化出拷贝构造函数呢?

【问题讨论】:

  • static some_class copy = some_class(); - 它将调用默认构造函数
  • 在问题中编写代码的提示:提供显示问题的代码。在这里,您提供了按您期望的方式工作的代码,然后您提供了有关如何修改它以显示问题的说明。那是倒退。方便人们复制和粘贴代码。
  • 一个相关问题:stackoverflow.com/q/12953127/580083。另请注意,在static some_class copy = origin; 中,copy 未分配给,它是复制构造的(这是两个不同的东西)。

标签: c++ language-lawyer copy-constructor copy-elision


【解决方案1】:

从C++17开始,保证了这种copy elision,编译器不仅允许,而且要求省略复制(或移动)构造,即使复制(或移动)构造函数有可观察的一面-效果。

在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。对象直接构建到存储中,否则它们将被复制/移动到。复制/移动构造函数不需要存在或可访问,因为语言规则确保不会发生复制/移动操作,即使在概念上也是如此:

  • 在变量的初始化中,当初始化表达式是与 变量类型:

    T x = T(T(T())); // only one call to default constructor of T, to initialize x
    
  • 在return语句中,当操作数是与函数返回类型相同的类类型(忽略cv-qualification)的prvalue时:

    T f() {
        return T();
    }
    
    f(); // only one call to default constructor of T
    

您的代码 sn-p 完全匹配这两种情况。

在 C++17 之前,编译器不是必需的,但允许省略复制(或移动)构造,即使复制/移动构造函数具有可观察到的副作用。请注意,这是一种优化,即使发生复制省略,复制(或移动)构造函数仍然必须存在且可访问。

另一方面,call() 不匹配复制省略的任何条件;它返回*this,它既不是纯右值也不是具有自动存储持续时间的对象,需要复制构造,不能省略。

【讨论】:

  • 即使在 C++17 之前,即使有副作用,也允许删除复制构造函数(只是不需要)。
猜你喜欢
  • 2016-09-25
  • 2011-09-12
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 1970-01-01
  • 2019-04-21
  • 2020-11-25
  • 2012-10-07
相关资源
最近更新 更多