假设我在 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行,被删除的副本所需的,无法访问
答案 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”并逐步调试您的对象。你应该看到这些字符串很容易。