Оператор преобразования разрезает мой объект

Я получаю неожиданное поведение из следующего кода:

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.

Может кто-нибудь, пожалуйста, пролить немного света?

2

Решение

Соответствующая цитата из стандарта в 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.

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]