我从以下代码中获得了意外行为:
struct Base
{
Base() {}
virtual ~Base() {}
virtual void foo() const = 0;
protected:
Base(const Base &) {}
};
struct Derived : public Base
{
Derived() {}
Derived(const Derived &other) : Base(other) {}
virtual void foo() const {}
};
struct NewDerived
{
operator const Derived() { return Derived(); }
};
void func(const Base &b)
{
b.foo();
}
int main()
{
func(NewDerived());
return 0;
}
使用MSVC2008,我在main()中得到了这个编译错误:
error C2248: 'Base::Base' : cannot access protected member declared in class 'Base'
为什么要尝试访问Base的复制构造函数?
如果我将Base的复制构造函数设置为public,则代码在运行时编译并切片返回值,并且在func()内调用foo()会触发一个名为error的纯虚函数。
有人可以放一点光吗?
答案 0 :(得分:6)
该标准的相关引用见8.5.3p5(C ++ 11):
具有类类型(即,T2是类类型),其中T1不是 与T2有关的引用,可以隐式转换为xvalue, class prvalue,或类型为“cv3 T3”的函数左值,其中“cv1 T1”为 参考兼容“cv3 T3”,然后引用绑定到 初始化表达式的值在第一种情况下和 在第二种情况下转换的结果(或者,在任何一种情况下,转换为 适当的基类子对象)。
示例:
struct A { };
struct B : A { } b;
extern B f();
const A& rca2 = f(); // bound to the A subobject of the B rvalue.
A&& rra = f(); // same as above
struct X {
operator B();
operator int&();
} x;
const A& r = x; // bound to the A subobject of the result of the conversion
在您的情况下,T1
为Base
,T2
为NewDerived
,T3
为Derived
。从上面的引用中,不应该调用复制构造函数 ,并且左值引用应该绑定到Base
子对象。
但请注意,在C ++ 03中,情况并非如此。在C ++ 03中,以下引用是相关的:
如果初始化表达式是rvalue,T2是类类型,和 “cv1 T1”与“cv2 T2”参考兼容,参考被绑定 到rvalue表示的对象(见3.10 [basic.lval])或者 该对象中的子对象。
...
否则,创建“cv1 T1”类型的临时并初始化 来自初始化表达式使用非引用的规则 复制初始化(8.5 [dcl.init])。然后绑定引用 暂时的。
第一个引用段落不适用,因为Base
与NewDerived
不兼容,因此只应用最后一段,这意味着必须创建一个临时Base
对象。因此,MSVC2008和gcc符合C ++ 03规则。