【问题标题】:Why Copy constructor is NOT called to copy the temporary object to the new defined object为什么不调用复制构造函数将临时对象复制到新定义的对象
【发布时间】:2013-05-20 22:30:00
【问题描述】:
#include <iostream>
using namespace std;

class Y {
public:
    Y(int ) {
        cout << "Y(int)\n";
    }
    Y(const Y&) {
        cout << " Y(const Y&)\n";
    }
};

int main() {
    Y obj1 = 2; // Line 1
}

输出:Y(int)

预期输出:Y(int) Y(常量 Y&)

问题> 根据我的理解,第1行会先创建一个临时对象Y(2),然后将临时对象赋值给obj1。因此,我希望 Y(int)Y(const Y&amp;) 都被调用。但是 vs2010 的输出只报告第一个(即Y(int))。为什么?

【问题讨论】:

    标签: c++ visual-studio-2010


    【解决方案1】:

    为什么?

    因为在某些条件下(由 C++11 标准的第 12.8/31 段指定)调用复制构造函数或移动构造函数可以被省略,即使这些特殊函数(或析构函数)有副作用:

    复制/移动的省略 称为复制省略的操作在以下情况下是允许的(可以组合到 消除多个副本):

    ——[...]

    ——当一个尚未绑定到引用 (12.2) 的临时类对象将被复制/移动时 对于具有相同 cv-unqualified 类型的类对象,可以通过以下方式省略复制/移动操作 将临时对象直接构造到省略的复制/移动的目标中

    ——[...]

    这是所谓的“as-if”规则的唯一例外,该规则通常限制编译器可以对程序执行的转换(优化)类型,以保留其可观察到的行为。

    请注意,上述机制称为复制省略 - 即使它实际上是对被省略的移动构造函数的调用。

    【讨论】:

      【解决方案2】:

      这是因为构造函数只有一个参数,没有标注explicit,所以编译器会自动转:

      Y obj1 = 2;
      

      进入:

      Y obj1(2);
      

      要防止这种行为,请使用:

      explicit Y(int ) {
          cout << "Y(int)\n";
      }
      

      (在您的情况下编译将失败)。

      【讨论】:

      • explicit 导致编译器错误的事实意味着它不会神奇地将= 2 转换为(2)explicit 所做的只是防止隐式转换为您的类型。
      • "为了防止这种行为 [...] 并且在您的情况下编译将失败。"
      【解决方案3】:

      这称为copy initializationY(int ) 是一个转换构造函数。那就是

      没有函数声明的单参数构造函数 说明符显式

      允许编译器删除多余的副本并使用您的conversion constructor。这意味着

      Y obj1 = 2; // Line 1
      

      等价于

      Y obj1(2); // Line 1
      

      【讨论】:

      • 请参阅 Stefano 的答案以进行进一步讨论(安迪的答案是正确的)。导致错误的explicit 意味着它们绝不是等价的。
      • 复制初始化不等同于直接初始化。前者在概念上是通过从初始化表达式 (2) 构造一个临时的,然后从该临时移动构造或复制构造 obj1 来实现的。但是,允许编译器根据 12.8/31 省略对移动构造函数或复制构造函数的调用(请参阅我的答案)
      猜你喜欢
      • 2011-01-20
      • 2012-03-16
      • 2010-12-22
      • 2016-10-10
      • 2015-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      相关资源
      最近更新 更多