I'm unsure whether this is a bug in the visual-c++ compiler or undefined behaviour.
Setup
struct DummyBase { virtual ~DummyBase() = default; };
struct DummyDerived : virtual public DummyBase {};
Just a class and a derived class using virtual inheritance
DummyDerived derived;
DummyBase* base = &derived;
std::cout << "Derived : " << &derived << std::endl;
std::cout << "Base : " << base << std::endl;
When casting DummyDerived* to DummyBase* the pointer is offset. This seems to be caused by virtual inheritance:
Derived : 00000000002CF838
Base : 00000000002CF840
Even though the pointer values are different a comparison will return true:
std::cout << "IsSame : " << (base == &derived) << std::endl << std::endl;
Output:
IsSame : 1
So far so good.
Problem
The problem arises in the following setup:
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; }
};
Here we have interfaces and implementations of classes with methods that return either DummyBase or DummyDerived overwritten via covariant return types. Again with virtual inheritance.
// Setup
DerivedReturner returner;
returner.Ptr = &derived;
IBaseReturner* baseReturner = &returner;
Now return DummyDerived* from DerivedReturner and DummyBase* from the same returner cast to IBaseReturner:
DummyDerived* derivedOriginal = returner.Get();
DummyBase* baseFromInterface = baseReturner->Get();
Compare Just like obove:
std::cout << "Derived Original : " << derivedOriginal << std::endl;
std::cout << "Base from Interface : " << baseFromInterface << std::endl;
Output
Derived Original : 00000000002CF838
Base from Interface : 00000000002CF838
Unlike above the pointers have the SAME value. Now compare them:
std::cout << "IsSame : " << (baseFromInterface == derivedOriginal) << std::endl;
Output:
IsSame : 0
The comparison returns false even tough the adresses are the same. This is expected since the pointer to DummyBase should have a different value.
Also when trying to dynamic_cast:
std::cout << dynamic_cast<DummyDerived*>(baseFromInterface);
And expception is thrown:
unknown file: error: C++ exception with description "Access violation - no RTTI data!" thrown in the test body.
Obviously since the pointer was not correctly offset.
Conclusion
It seems as though when calling IBaseReturner::Get the visual-c++ compiler fails to do the necessary pointer arithmetic to cast DummyDerived* to DummyBase*. This happens in vs2013 and vs2015 (didn't try any other version). Also when compiled with gcc it works fine.
Question
While the setup might be a bit complex the question it rather simple:
Is this a msvc bug or am I causing undefined behavoiur?
Added an online example : http://rextester.com/KHZXGQ27304
Your code does not have an undefined behavior (both GCC and Clang compile and execute it fine, demo: https://gcc.godbolt.org/z/q91vbhjTh), and its run fails due to a bug in MSVC, which is still present in Visual Studio 2019 and 2022.
Reported MSVC bug: https://developercommunity.visualstudio.com/t/Invalid-code-generated-for-virtual-inher/1598214
From their response: