为什么在这种情况下不调用复制构造函数?

时间:2009-11-18 18:40:05

标签: c++ constructor copy-constructor

以下是小代码段:

class A
{
public:
    A(int value) : value_(value)
    {
        cout <<"Regular constructor" <<endl;
    }

    A(const A& other)   : value_(other.value_)  
    {
        cout <<"Copy constructor" <<endl;
    }

private:
    int value_;
};
int main()
{
    A a = A(5);
}

我假设输出为“常规构造函数”(对于RHS),后面是LHS的“复制构造函数”。所以我避免使用这种样式,并始终将类的变量声明为A a(5);。但令我惊讶的是,上面的代码中永远不会调用复制构造函数(Visual C ++ 2008)

有人知道这种行为是编译器优化的结果,还是C ++的一些记录(和可移植)功能?感谢。

4 个答案:

答案 0 :(得分:14)

从另一条评论:“所以默认情况下我不应该依赖它(因为它可能取决于编译器)”

不,它实际上并不依赖于编译器。任何值得一粒沙子的编译器都不会浪费时间构建A,然后将其复制过来。

在标准中明确指出,T = x;完全可以接受T(x);。 (§12.8.15,第211页)使用T(T(x))执行此操作显然是多余的,因此它会移除内部T

要获得所需的行为,您需要强制编译器默认构造第一个A:

A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);

答案 1 :(得分:7)

我正在研究这个问题以回答另一个被作为欺骗而被关闭的问题,所以为了不让工作浪费,我正在回答这个问题。

A a = A(5)形式的声明称为变量a复制初始化。 C ++ 11标准,8.5 / 16表示:

  

使用初始化表达式调用所选函数   它的论点;如果函数是构造函数,则调用初始化a   临时的目标类型的cv不合格版本。该   临时是一个prvalue。通话的结果(这是暂时的   然后根据构造函数用于直接初始化   对于上面的规则,作为目标的对象   副本初始化。 在某些情况下,允许实施   消除这种直接初始化中固有的复制   将中间结果直接构造到对象中   初始化;见12.2,12.8

这意味着编译器会查找相应的构造函数来处理A(5),创建一个临时文件并将其临时复制到a。但在什么情况下可以删除副本?

让我们看看12.8 / 31的内容:

  

当满足某些条件时,允许省略实现   复制/移动类对象的构造,即使复制/移动   对象的构造函数和/或析构函数有副作用。   在这种情况下,实施处理的来源和目标   省略了复制/移动操作,只是两种不同的引用方式   对于同一个对象,该对象的破坏发生在   稍后两个物体被摧毁的时间   没有优化。这种复制/移动操作的省略,   在下列情况下允许使用 copy elision   (可以合并以消除多个副本):

     

[...]

     
      
  • 当复制/移动尚未绑定到引用(12.2)的临时类对象时   对于具有相同cv-unqualified类型的类对象,可以进行复制/移动操作   通过将临时对象直接构造到省略的copy / move
  • 的目标中而省略   

考虑到所有这些,这就是表达式A a = A(5)

的情况
  1. 编译器会看到带有复制初始化的声明
  2. 选择A(int)构造函数来初始化临时对象
  3. 因为临时对象绑定到引用,并且它与复制初始化表达式中的目标类型具有相同的类型A,所以允许编译器直接将对象构造成a,省略临时

答案 2 :(得分:4)

您可以从临时a获得A(5)复制初始化。根据C ++标准12.2 / 2,允许实现跳过调用复制构造函数。

答案 3 :(得分:-1)

A a = A(5);

这一行相当于

A a(5);

尽管它具有函数式外观,但第一行只是用参数5构造a。不涉及复制或临时。从C ++标准,第12.1.11节:

  

功能表示法类型转换(5.2.3)可用于创建其类型的新对象。 [注意:   语法看起来像构造函数的显式调用。 - 后注]