转换运算符正在切割我的对象

时间:2013-05-23 20:22:12

标签: c++ object-slicing conversion-operator

我从以下代码中获得了意外行为:

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的纯虚函数。

有人可以放一点光吗?

1 个答案:

答案 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

在您的情况下,T1BaseT2NewDerivedT3Derived。从上面的引用中,不应该调用复制构造函数 ,并且左值引用应该绑定到Base子对象。

但请注意,在C ++ 03中,情况并非如此。在C ++ 03中,以下引用是相关的:

  

如果初始化表达式是rvalue,T2是类类型,和   “cv1 T1”与“cv2 T2”参考兼容,参考被绑定   到rvalue表示的对象(见3.10 [basic.lval])或者   该对象中的子对象。

...

  

否则,创建“cv1 T1”类型的临时并初始化   来自初始化表达式使用非引用的规则   复制初始化(8.5 [dcl.init])。然后绑定引用   暂时的。

第一个引用段落不适用,因为BaseNewDerived不兼容,因此只应用最后一段,这意味着必须创建一个临时Base对象。因此,MSVC2008和gcc符合C ++ 03规则。