2013-12-08 65 views
8

拥有一个公共拷贝构造函数将会使小程序 编译,但不会显示副本“Copy”。为什么即使未被调用也需要公共拷贝构造函数?

#include <iostream> 

class X 
{ 
    public: 
    X(int) { std::cout << "Construct" << std::endl; } 

    // Having a public copy constructor will make the little program 
    // compile, but not showing the side effect "Copy". 

    private: 
    X(const X&) { std::cout << "Copy" << std::endl; } 

    private: 
    X& operator = (const X&); 
}; 

int main() { 
    X x = 1; 
    return 0; 
} 
+2

X x = 1意味着X x(X(1))就我所知,但它被优化到X x(1); – odinthenerd

+3

这是必需的,以便C++代码在可能或不可以自行决定执行copy elision的实现之间是可移植的。 – jrok

+1

尝试使用'-fno-elide-constructors'标志进行编译。 –

回答

4

下面是所涉及的C++标准的相关位:

[dcl.init]/16,子弹6,子子弹1:如果初始化直接初始化,或者如果它是复制初始化其中源类型的CV-不合格的版本是相同的类,或者派生类的,类目的地的,构造函数被认为.... 如果没有构造适用,或过载分辨率是模棱两可,初始化不合格。 [强调]

换句话说,如果一个编译器优化可能的Elid复制,因为没有适用的构造也没关系,是形成不良的初始化。当然,一旦你让副本constuctor公开,下面的部分适用:

[class.copy]/31:当满足一定的条件,实现允许省略一类的复制/移动建设对象,即使该对象的复制/移动构造函数和/或析构函数具有副作用....在以下情况下,允许复制/移动操作的复制(称为复制省略)(可组合以消除多个副本):

bullet 3:当未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualified类型的类对象时,可以省略复制/移动操作通过构建临时对象d直接进入被删除的拷贝/移动的目标

2

我最好的猜测是这是一个编译器优化。如果你有一个拷贝构造函数,就有一条以这种方式声明一个对象的有效路径。考虑这样做明确:

int main() { 
    X x(X(1)); 
    return 0; 
} 

或者更明确:

int main() { 
    X intermediate(1); 
    X x(intermediate); 
    return 0; 
} 

因此,而不是使用operator=,它知道你想自申报来初始化对象。本质上,编译器是“聪明的”。最后,再次优化了该到了这一步:

int main() { 
    X x(1); 
    return 0; 
} 

因此,编译器计算出后,“你想干什么”这成为对象的标准初始化。

编辑

为了完整起见,请注意,如果你试试这个:

int main() { 
    X x = 1; 
    x = 2; 
    return 0; 
} 

您将看到的问题与私人operator=。明确地说,即使=出现在代码中,这一点很重要,因为operator=在上面的初始化初始化问题中从未实际使用过。

+0

哎呀应该没有回应,而不是评论;)恭喜。 – odinthenerd

+0

@PorkyBrain:如果'X x(X(1))'被优化为'X x(1)',那么您的评论会在这里解决我的问题,谢谢 – RageD

+0

这里的代码完全不使用'operator ='。 'X x = 1'是'x'的直接初始化,并且不涉及赋值;它可能(但不必)调用复制构造函数。 –

2

您初始化是由编译器优化到:

X x(1) 

这是一种复制省略的,并且是由标准允许的,即使它可以消除副作用,如你所看到的。

从C++ 03标准部分12.8:

当满足特定条件时,一种实现被允许省略 一个类对象的拷贝结构,即使拷贝构造 和/或该对象的析构函数有副作用。在这种情况下, 实现将被忽略的副本 操作的源和目标视为指向同一个对象的两种不同方式,并且该对象的销毁发生在两个时间点的后期,其中两个如果没有进行优化,对象就会被销毁。 111)在以下情况下(这可以合并以消除多个 副本)允许在 中复制操作的这种省略: - 在具有类返回类型的函数中的语句声明, 当表达式是非重复类型的名称时,通过将自动对象 直接构造到函数的返回值 中 - 当没有绑定到的临时类>对象时,可以省略 复制操作,该对象具有与函数返回类型相同的cv不合格类型一个参考(12。2) 将被复制到具有相同的CV-不合格类型的类对象时,复制 操作可以通过构建节奏-郭宝宏对象 直接进入省略拷贝

第二种情况的目标可以省略这是我们在这里。

+1

这不是一个赋值;这是一个初始化。 –

8

您已经使用所谓的“拷贝初始化”(在[decl.init]定义)。所述所定义的含义是构建使用int构造类型X的临时对象,然后使用拷贝构造初始化从临时x

然而,该标准还允许在这种情况下,所谓的“拷贝构造函数省音”(在[class.copy]定义)的优化。如果应用该优化,则不存在临时性。 x使用int构造函数构造,就像您编写所谓的“直接初始化”X x(1);一样。

为了让你不小心写代码时,应用优化,编译但不是当它不是,该标准要求拷贝构造函数必须是偶数,如果它会被省略访问。因此,构造函数必须是公开的,即使(使用您使用的编译器和选项)它不会被调用。

在C++ 11层移动的构造都认为,和有资格获得省音了。然而这个类X没有移动构造函数,所以C++ 11和C++ 03在这个例子中是相同的。

+2

简而言之,语义决定了一个副本,因此程序在优化之前应首先符合语义。 –

相关问题