2013-05-23 4 views
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, я получаю эту ошибку компиляции в основном():

error C2248: 'Base::Base' : cannot access protected member declared in class 'Base' 

Почему это пытаясь получить доступ к конструктору копирования Base?

Если я создаю конструктор копии базы данных общедоступным, код компилирует и разрезает возвращаемое значение во время выполнения, а вызов функции foo() внутри func() запускает чистую виртуальную функцию с именем error.

Может кто-то пролить немного света?

ответ

6

Соответствующая цитата из стандарта в 8.5.3p5 (11 C++):

имеет тип класса (то есть, Т2 является тип класса), где T1 не является справочно-связанной к T2 и может быть неявно преобразован в значение xvalue, класс prvalue или значение функции lvalue типа «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. Из приведенной выше цитаты, конструктор копирования не должен быть вызван, а ссылка lvalue должна привязываться к подобъекту Base.

Однако обратите внимание, что в C++ 03 это было не так. В C++ 03, следующие цитаты были уместны:

Если выражение инициализатора является Rvalue с Т2 типом класса, и «CV1 T1» является ссылкой совместит с «CV2 T2» ссылка привязано к объекту, представленному rvalue (см. 3.10 [basic.lval]), или к под-объекту внутри этого объекта.

...

В противном случае временный типа «CV1 T1» создается и инициализируется из выражения инициализатора, используя правила для нереференсного инициализации копии (8,5 [ dcl.init]). Ссылка затем привязана к временному.

Первый абзац, который не применяется, поскольку Base не ссылаться совместимы с NewDerived, поэтому только последний пункт применяется, что означает временный Base объект должен быть создан.Поэтому MSVC2008 и gcc соответствуют правилам C++ 03.

+0

+1 За рыть в историю;) –

+1

В качестве примечания, почему следующие незначительные изменения в код позволяет ему составить, по крайней мере, в VS: 'Int основного() { сопзИте Base & б (NewDerived()); } ' Любые идеи? – AlexK

+1

@AlexK: Вас поразил [MVP] (http://en.wikipedia.org/wiki/Most_vexing_parse). Попробуйте 'const Base & b ((NewDerived()));' (обратите внимание на дополнительные скобки). –