Я не уверен, является ли это ошибкой в компиляторе 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
Задача ещё не решена.
Других решений пока нет …