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

ked*_*ede 2 c++ conversion-operator object-slicing

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

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;
}
Run Code Online (Sandbox Code Playgroud)

使用MSVC2008,我在main()中得到此编译错误:

error C2248: 'Base::Base' : cannot access protected member declared in class 'Base'
Run Code Online (Sandbox Code Playgroud)

为什么要尝试访问Base的复制构造函数?

如果我将Base的复制构造函数设置为public,则代码在运行时编译并切片返回值,并且在func()内调用foo()会触发一个名为error的纯虚函数.

有人可以请一点光吗?

Jes*_*ood 6

标准的相关引用是在8.5.3p5(C++ 11)中:

具有类类型(即,T2是类类型),其中T1与T2不是引用相关的,并且可以隐式转换为类型为"cv3 T3"的xvalue,类prvalue或函数左值,其中"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
Run Code Online (Sandbox Code Playgroud)

在你的情况下,T1Base,T2NewDerived,T3Derived.从上面的引用中,应该调用复制构造函数,并且左值引用应该绑定到Base子对象.

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

如果初始化表达式是rvalue,T2是类类型,并且"cv1 T1"与"cv2 T2"引用兼容,则引用绑定到由rvalue表示的对象(参见3.10 [basic.lval])或到该对象内的子对象.

...

否则,使用非参考副本初始化的规则(8.5 [dcl.init])从初始化表达式创建并初始化类型为"cv1 T1"的临时类型.然后将引用绑定到临时.

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