2010-07-14 50 views
8
#include <iostream> 

class A { 
    public: 
    A(){ cerr << "A Constructor" << endl; } 
    ~A(){ cerr << "A Destructor" << endl; } 
    A(const A &o){ cerr << "A Copy" << endl; } 
    A& operator=(const A &o){ cerr << "A Assignment" << endl; return *this; } 
}; 


class B : public A { 
    public: 
    B() : A() { cerr << "B Constructor" << endl; } 
    ~B(){ cerr << "B Destructor" << endl; } 
    private: 
    B(const B &o) : A() { cerr << "B Copy" << endl; } 
    B& operator=(const B &o){ cerr << "B Assignment" << endl; return *this; } 
}; 

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

在GCC 4.2,我得到这个消息:意外const引用行为

In function 'int main()': 
Line 16: error: 'B::B(const B&)' is private 
compilation terminated due to -Wfatal-errors. 

如果我删除了 “私人” 从B,我得到的输出我想到:

A Constructor 
A Constructor 
B Constructor 
B Destructor 
A Destructor 
A Destructor 

我的问题是:为什么要制作一个不被称为private的方法,无论这个代码是否编译?这是标准授权吗?有没有解决方法?

+0

我不明白为什么这不应该编译。 FWIW,Comeau与我同意。 – sbi 2010-07-14 18:22:34

+1

@sbi:奇怪的是,Comeau拒绝禁用C++ 0x扩展的代码,但接受启用了C++ 0x扩展的代码。 – 2010-07-14 18:23:26

+0

g ++ 4.4.2,FWIW没有错误。 – 2010-07-14 18:44:20

回答

4

当前标准(C++ 03)中的重要语言似乎在§8.5.3中,它解释了如何初始化引用(In这些引号T1是正在初始化的引用的类型,T2是初始化表达式的类型)。

如果初始化表达式是一个rvalue,与T2类类型,和“cv1 T1”是参考兼容“cv2 T2”的引用在下列方式中的一种(结合的选择是实现定义):

- 引用绑定到由右值(参见3.10)表示的对象或该对象内的子对象。

- 创建了一个类型为“cv1 T2”的临时文件[sic],并调用构造函数将整个右值对象复制到临时文件中。引用绑定到临时对象或临时对象内的子对象。

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

因此,即使实现将引用直接绑定到临时对象,复制构造函数也必须是可访问的。

请注意,根据CWG defect 391的分辨率,这在C++ 0x中发生了变化。新的语言读取(N3092§8.5.3):

否则,如果T2是一个类类型和

- 初始化表达式是一个右值和“cv1 T1”与“cv2 T2参考兼容“

- T1不引用相关于T2和初始化表达式可以隐式转换到类型的右值” cv3 T3"(这种转换通过枚举适用转换函数(13.3.1.6选择的)和选择最好的一个通过overl oad resolution(13.3)),

然后,引用在第一种情况下绑定到初始化表达式rvalue,并在第二种情况下绑定到作为转换结果的对象(或者在任一情况下,对象的基类子对象)。

第一种情况适用,并且引用“直接绑定”到初始化表达式。

+0

决议记录/标准讨论子对象。显然子对象有多态性,这让我有点困惑。出于好奇,标准是否在谈论缩小某处?我在2005年的工作副本中找不到任何东西,但我想我只是不知道要搜索什么。我正在看'标准转换'和复制构造函数部分。 – 2010-07-14 20:15:16

+0

@Zachary:C++标准的一个“特征”是主题通常分布在六个不同的部分,如果您不熟悉可能的情况,就很难在其中找到信息。在这种情况下,Declarator Initializers(§8.5)中的子条款非常重要,因为您正在初始化引用。关于临时对象的§12.2很重要,因为你有一个临时的类型对象。通常,重要的部分是在一段中间的单个短句。有时我发现它有助于搜索关键词(例如,“参考”)。 – 2010-07-14 21:21:25

1

我觉得这确实是一个编译器bug,gcc似乎认为是复制初始化。改用直接初始化:

const A& b(B()); 

在拷贝初始化拷贝构造函数调用总是优化掉(复制省略的一个实例),然后没有可用。