Я получаю неожиданное поведение из следующего кода:
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 общедоступным, код скомпилирует и разделит возвращаемое значение во время выполнения, и вызов функции foo () внутри func () вызовет чисто виртуальную функцию с именем error.
Может кто-нибудь, пожалуйста, пролить немного света?
Соответствующая цитата из стандарта в 8.5.3p5 (C ++ 11):
имеет тип класса (т.е. T2 является типом класса), где 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, с T2 тип класса, и
«Cv1 T1» совместим с «cv2 T2», ссылка привязана
к объекту, представленному значением r (см. 3.10 [basic.lval]) или к
подобъект внутри этого объекта.
…
Иначе, создается временный тип cv1 T1 и инициализирован
из выражения инициализатора, используя правила для не-ссылки
инициализация копии (8.5 [dcl.init]). Ссылка тогда связана с
временный
Первый цитируемый абзац не применяется, потому что Base
не совместим с NewDerived
, так что применяется только последний абзац, что означает временный Base
объект должен быть создан. Поэтому MSVC2008 и gcc соответствуют правилам C ++ 03.
Других решений пока нет …