使用C ++中的派生类中的私有拷贝构造函数进行对象切片

时间:2009-08-20 08:09:56

标签: c++ compiler-errors

假设我在 test.cxx 中有以下内容(我故意在 1 进行对象切片):

class A {
};

class B : public A {
  // prevent copy construction and assignment
  B(const B& other);
  B& operator=(const B& other);
public:
  explicit B(){}
};

class C {
  A m_a;
public:
  explicit C() : m_a( B() ) {} // 1
};

我希望这可以工作,就像在 1 中一样,应该调用A类的复制构造函数(这里是编译器生成的和公共的)。这段代码也适用于最近的编译器(我试过g ++ - 4.4和Intel 11.0),但是较旧的编译器(例如g ++ - 4.2和g ++ - 4.0)试图调用B的拷贝构造函数,我声明它是私有的,导致:

test.cxx: In constructor ‘C::C()’:
test.cxx:7: error: ‘B::B(const B&)’ is private
test.cxx:16: error: within this context

现在,在我的构建系统中,我想检查编译器是否支持上面的代码。但问题是这个符合标准的代码吗?那么这个测试的正确名称是什么?

编辑:对不起,英特尔编译器版本10.1和11.0都发出以下内容:警告#734:“B :: B(const B&)”(声明于第6行,被删除的副本所需的,无法访问

5 个答案:

答案 0 :(得分:4)

在这种情况下我敢与Comeau不同意。实际上,以下代码无法按预期编译,因为将rvalue绑定到const引用需要可访问的复制构造函数。

class A {
};

class B : public A {
  B(const B& other);
  B& operator=(const B& other);
public:
  explicit B(){}
};

int main()
{
    A const & a = B();
}

按照8.5.3 / 2,“[...]参数传递(5.2.2)和函数值返回(6.6.3)是初始化,”因此代码应该是格式错误。

编辑:我仍然坚信,根据C ++ 03,代码格式不正确。但是,我刚刚阅读了C ++ 0x工作草案的相关部分,似乎它不再需要复制构造函数可用。也许这就是你从gcc-4.2转到gcc-4.3时代码开始编译的原因。

修改:为了澄清,B::B(const B &)必须可访问的原因是B()A::A(const A &)的第一个参数绑定(即m_a当然,在初始化{{1}}时调用。

编辑:关于C ++ 03和C ++ 0x之间的区别,litb足以找到relevant defect report

答案 1 :(得分:1)

如果没有可用的复制构造函数,似乎较旧的g ++编译器无法通过引用传递临时对象:

class A { 
  A(const A& other);
  A& operator=(const A& other);
public:
  explicit A(){}
};
void f( const A& a ) {}
int main() {
  A a;
  f( a );    // fine
  f( A() );  // fails
}

答案 2 :(得分:1)

我认为它是C ++ 0x中符合标准的代码,但不适用于C ++ 03。

我将测试命名为“从rvalue复制构造”。

据报道这是一个错误,但是gcc人认为here这是正确的行为,并提供对标准的引用。

[dcl.init.ref] / 5,bullet 2,sub-bullet 1

  

如果初始化表达式在   rvalue,T2为类类型,“cv1   T1“与”cv2参考兼容   T2“参考被绑定在其中一个   以下方式(选择是   实施定义):
     - 引用绑定到右值表示的对象(请参阅   3.10)或该对象内的子对象      - 创建一个临时类型为“cv1 T2”[sic],构造函数为   调用复制整个右值   对象进入临时。该   引用绑定到临时或   到临时的子对象。

     

将用于的构造函数   使副本可以调用   该副本是否真实   完成

C ++ 0x标准消除了歧义,引用始终与rvalue表示的对象有关,不需要构造函数可访问。

答案 3 :(得分:0)

我不能指出你在标准的特定部分,但它对我来说当然看起来不错,并编译Comeau C++以及编译器你 提到。至于这样一个测试的名称,我猜“编译兼容性”和任何东西一样好。

答案 4 :(得分:0)

有效。调用B()构造一个B对象;调用B的默认编译器生成的公共构造函数。

构造C对象时,将构造一个A对象。由于它是一个值,因此将考虑静态类型,并且将对从A派生的任何对象进行切片,并且该切片将用于复制构造A对象。 A类具有编译器提供的公共拷贝构造函数。编译器看到B对象也是A类型。编译器只关心构造A对象的副本,它知道它可以这样做,所以它使用A拷贝构造函数将A对象复制到B对象中: :m_a对象。即由于静态打字而切片。

查看这些对象的内存是有益的。将一个char *数据成员初始化为“我是一个A”并将B中的char *数据成员初始化为“我是一个B”并逐步调试您的对象。你应该看到这些字符串很容易。