MSVC: ковариантные типы возврата и виртуальное наследование

Я не уверен, является ли это ошибкой в ​​компиляторе visual-c ++ или неопределенным поведением.

Настроить

struct DummyBase { virtual ~DummyBase() = default; };
struct DummyDerived : virtual public DummyBase {};

Просто класс и производный класс, использующий виртуальное наследование

DummyDerived derived;
DummyBase* base = &derived;

std::cout << "Derived : " << &derived << std::endl;
std::cout << "Base    : " << base << std::endl;

При кастинге DummyDerived* в DummyBase* указатель смещен. Кажется, это вызвано виртуальным наследованием:

Derived : 00000000002CF838
Base    : 00000000002CF840

Даже если значения указателя отличаются, сравнение вернет true:

std::cout << "IsSame  : " << (base == &derived) << std::endl << std::endl;

Выход:

IsSame  : 1

Все идет нормально.

проблема

Проблема возникает в следующей настройке:

 struct IBaseReturner
{
virtual DummyBase* Get() = 0;
};

struct IDerivedReturner : public virtual IBaseReturner
{
virtual DummyDerived* Get() = 0;
};

struct BaseReturner : public virtual IBaseReturner
{
};

struct DerivedReturner : public BaseReturner, public virtual IDerivedReturner
{
DummyDerived* Ptr;
virtual DummyDerived* Get() override { return Ptr; }
};

Здесь у нас есть интерфейсы и реализации классов с методами, которые возвращают либо DummyBase или же DummyDerived перезаписывается с помощью ковариантных типов возврата. Опять с виртуальным наследованием.

// Setup
DerivedReturner returner;
returner.Ptr = &derived;
IBaseReturner* baseReturner = &returner;

Сейчас вернусь DummyDerived* от DerivedReturner а также DummyBase* от того же вернувшегося IBaseReturner:

DummyDerived* derivedOriginal = returner.Get();
DummyBase* baseFromInterface = baseReturner->Get();

Сравните, как и выше:

std::cout << "Derived Original    : " << derivedOriginal << std::endl;
std::cout << "Base from Interface : " << baseFromInterface << std::endl;

Выход

Derived Original    : 00000000002CF838
Base from Interface : 00000000002CF838

В отличие от вышеперечисленных указатели имеют то же значение.
Теперь сравните их:

std::cout << "IsSame  : " << (baseFromInterface == derivedOriginal) << std::endl;

Выход:

IsSame  : 0

Сравнение возвращает false даже жесткие адреса одинаковы. Это ожидается, так как указатель на DummyBase должен иметь другое значение.
Также при попытке к dynamic_cast:

std::cout << dynamic_cast<DummyDerived*>(baseFromInterface);

И исключение брошено

unknown file: error: C++ exception with description "Access violation - no RTTI data!" thrown in the test body.

Очевидно, так как указатель был неправильно смещен.

Заключение

Кажется как будто при звонке IBaseReturner::Get компилятор visual-c ++ не может выполнить необходимую арифметику указателей для приведения DummyDerived* в DummyBase*, Это происходит в vs2013 и vs2015 (не пробовал никакой другой версии). Также при компиляции с gcc все работает нормально.

Вопрос

Хотя установка может быть немного сложной, вопрос довольно прост:

Это ошибка msvc или я вызываю неопределенное поведение?

Добавлен онлайн пример: http://rextester.com/KHZXGQ27304

3

Решение

Задача ещё не решена.

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

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

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector