In the following code:
#include <iostream>
#include <thread>
#include <mutex>
#include <functional>
struct IView {
virtual void setOnClick() = 0;
};
struct ITextView : IView {
virtual void setText() = 0;
};
struct IButton : ITextView {
virtual void setRadius() = 0;
};
struct View : IView {
int i = 1;
virtual void setOnClick() override {
std::cout << "setting OnClick! i: " << i << std::endl;
};
};
/// Works as is
/// But if make "TextView : View, ITextView" - have segfault on the run
struct TextView : ITextView, View {
int j = 2;
virtual void setText() override {
std::cout << "setting text! i: " << i << " j: " << j << std::endl;
};
// forward IView
virtual void setOnClick() override {
View::setOnClick();
}
};
int main() {
TextView tv;
void* ptr = &tv; // I need to pass raw pointer, and then restore "interface" from it
ITextView* itv = static_cast<ITextView*>(ptr); // I don't need safety checks here
itv->setOnClick();
itv->setText();
return 0;
}
If I change TextView
s inheritance order I'll have segfault on itv->setText();
call.
Why is it important? Can I use static_cast
here, or I have UB here? As I understand dynamic_cast
only required with virtual inheritance, and this, as I can tell, not that case.
As it stands, you convert implicitly from TextView*
to void*
, then explicitly from void*
to ITextView*
. These conversions do not perform any pointer adjustment when casting from/to void*
, so you end up with a pointer of type ITextView*
which actually points to a TextView
(not to its ITextView
subobject!): undefined behaviour ensues.
The solution is to take care of always using the exact same type on both "sides" of the void*
:
TextView tv;
void* ptr = static_cast<ITextView*>(&tv); // Adjust, then convert to void*
ITextView* itv = static_cast<ITextView*>(ptr);