2

假设我在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:在构造函数'C::C()'中:
test.cxx:7:错误:'B::B(const B&)' 是私有的
test.cxx:16:错误:在此上下文中

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

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

4

5 回答 5

4

在这种情况下,我敢于不同意科莫。事实上,以下代码无法按预期编译,因为将右值绑定到 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在初始化时调用)。

编辑:关于 C++03 和 C++0x 之间的区别,litb 很友好地找到了相关的缺陷报告

于 2009-08-20T08:31:15.517 回答
1

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

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

这被报告为错误,但 gcc 人在这里争辩说这是正确的行为并参考了标准。

[dcl.init.ref]/5,项目符号 2,子项目符号 1

如果初始化表达式在右值中,T2 是类类型,并且“cv1 T1”与“cv2 T2”引用兼容,则引用以下列方式之一绑定(选择由实现定义):
- 引用是绑定到由右值表示的对象(参见 3.10)或该对象中的子对象。
- 创建一个“cv1 T2”[sic] 类型的临时对象,并调用构造函数将整个右值对象复制到临时对象中。引用绑定到临时对象或临时对象中的子对象。

无论副本是否实际完成,用于制作副本的构造函数都应该是可调用的。

C++0x 标准消除了歧义,并且引用始终绑定到由右值表示的对象,不需要构造函数可访问。

于 2009-08-20T09:45:50.767 回答
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
}
于 2009-08-20T09:22:52.543 回答
0

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

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

查看这些对象的内存是有启发性的。将 char * 数据成员放入初始化为“我是 A”的 A 中,将 char * 数据成员放入初始化为“我是 B”的 B 中,并逐步通过调试器来监控您的对象。你应该很容易看到这些字符串。

于 2009-08-20T11:22:13.987 回答
0

我不能指出标准的特定部分,但它在我看来确实不错,并且可以使用Comeau C++以及您提到的编译器进行编译。至于这样的测试的名称,我猜“编译器兼容性”和任何东西一样好。

于 2009-08-20T08:14:58.453 回答