私有拷贝构造函数/赋值运算符和复制初始化

时间:2011-06-29 16:40:41

标签: c++ boost

这是this question

的后续内容

在下面的代码中,为什么第1行编译而第2行和第3行不编译(使用visual C ++ 2010)

class ABase
{
protected:
    ABase() {}
    ~ABase() {}
private:
    ABase( const ABase& );
    const ABase& operator=( const ABase& );
};

class A : ABase
{
};

class B
{
public:
    B() {}
    ~B() {}
private:
    B( const B& );
    const B& operator=( const B& );
};

int main( void )
{
    A a = A(); // line 1
    A a2( a ); // line 2
    B b = B(); // line 3

    return 0;
}

(注意BA是boost :: noncopyable的副本)

修改 我的问题是不知道为什么第2行和第3行不能编译(我知道,复制构造函数是私有的),但为什么第1行呢。

6 个答案:

答案 0 :(得分:6)

确实第1行不应该编译。

显然,在这种情况下,vc ++ 2010在执行语言规则时遇到问题(可能是因为它们与基类有关,而与对象本身无关)。

关于第1行的g ++诊断消息非常清楚

ncopy.cpp: In copy constructor ‘A::A(const A&)’:
ncopy.cpp:7: error: ‘ABase::ABase(const ABase&)’ is private
ncopy.cpp:12: error: within this context
ncopy.cpp: In function ‘int main()’:
ncopy.cpp:27: note: synthesized method ‘A::A(const A&)’ first required here 

答案 1 :(得分:5)

编译器在接受第一次使用时出错了。即使复制被删除,也必须可以访问复制构造函数,以使代码正确。

在这种特殊情况下,A中有一个隐式声明的复制构造函数:

  

§12.8/ 4如果类定义没有显式声明一个拷贝构造函数,则会隐式声明一个。

这是隐含的定义:

  

§12.8/ 7隐式声明的复制构造函数是隐式定义的,如果它用于从其类类型的对象或从类类型108派生的类类型的副本初始化其类类型的对象。 [注意:即使实现省略了它的使用(12.2),也会隐式定义复制构造函数。如果隐式定义了复制构造函数的类具有以下内容,则程序格式错误

     

- 具有无法访问或模糊的复制构造函数的类类型(或其数组)的非静态数据成员,或

     

- 具有无法访问或模糊复制构造函数的基类。

答案 2 :(得分:2)

ABase( const ABase& );

复制构造函数是私有的,因此无法使用此私有复制构造函数创建类对象的副本,从而导致错误。

A a = A(); // line 1

使用A::A(const A&)创建新的A对象。 A源自ABase,并且会调用 ABase::ABase(const ABase&)在它的构造函数中,它是私有的,也不会编译。

以下是Ideone上的output。它甚至不能在gcc上编译。

为什么它适用于Visual Studio?
原因是可视化C ++编译器可能存在返回值优化,它使复制构造函数无效。

根据C ++标准, 12.8复制类对象第15节

当满足某些条件时,允许实现省略类对象的复制结构,即使该对象的复制构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象的后期时间。在没有优化的情况下销毁.111)

请参阅我的回答 here ,其中引用了标准和此方面的示例代码。

A a2( a ); // line 2

由于ABase::ABase(const ABase&)是私有的相同原因而无法编译。

 B b = B(); // line 3

无法编译,因为B( const B& );是私有的。

答案 3 :(得分:1)

第1行是返回值优化(编译器发现不需要为A()创建临时变量,并使用复制构造函数/赋值运算符分配给变量a)。但是,这不能在GCC(版本4.2.1)上编译,应该避免使用。

第2行无法编译,因为在这种情况下编译器不会为您生成赋值运算符。正如您所料,第3行没有编译。

Reagan摘要:第1行有效,因为它是Microsoft,其他行为符合预期。

答案 4 :(得分:1)

为什么第1行编译?因为你的编译器坏了;它不应该, 按照标准。虽然您的班级A含有隐含的内容 声明的拷贝构造函数,§12.8/ 7的标准规定了An 隐式声明的复制构造函数将被隐式定义 用于初始化对象(如在A a = A();中),并且程序是 如果构造函数是,则形成错误(因此需要诊断) 隐式定义和基类具有不可访问或模糊 复制构造函数。甚至还有一张纸条说即使这样也是如此 实现省略了复制构造函数。你是编译器不是 足够远:它看到隐含声明的公共副本 构造函数,但它不会尝试隐式定义它,即使它 标准明确表示应该。

答案 5 :(得分:0)

我相信这是因为构造函数是受保护的,而不是私有的。编译器提供的类A中的构造函数可以自由地调用类ABase的受保护构造函数,因此它可以工作。

此外,第1行不是复制构造函数。带赋值的声明是一种特殊情况,它被转换为构造函数。