2

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

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 的复制构造函数,代码会在运行时编译并切片返回值,并且在 func() 中对 foo() 的调用会触发一个名为 error 的纯虚函数。

有人可以解释一下吗?

4

1 回答 1

6

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

具有类类型(即 T2 是类类型),其中 T1 与 T2 没有引用相关,并且可以隐式转换为类型为“cv3 T3”的 xvalue、类纯右值或函数左值,其中“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 中,以下引用是相关的:

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

...

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

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

于 2013-05-23T21:11:13.967 回答