Here is the code:
#include <iostream>
struct Parent
{
virtual ~Parent() = default;
};
class Buddy
{
public:
virtual ~Buddy() = default;
Buddy(Parent& p) : parent_ { p } {}
bool IsHovered() const { return is_hovered_; }
private:
bool is_hovered_ = false;
Parent& parent_;
};
class Child : public Parent, public Buddy
{
public:
Child() : Buddy { *this } {}
bool BuddyIsHovered() const { return IsHovered(); }
};
int main()
{
Child c;
// Expected 'false', but 'true' is always printed.
std::cout << std::boolalpha << c.BuddyIsHovered() << '\n';
}
I can assume that the problem (may be it's an undefined behaviour) occurred because of multiple inheritance and mutual usage of Child and Buddy. From the other side, I also have assumptions that:
Child;Child constructor; at this point it seems to me, that compiler should know correct addresses for all 3 classes;Parent, Buddy, Child. So, when the Child constructor_ is running, Buddy's memory has been already initialized, and thus Buddy can be used. I mean, Child's code can use Buddy's code, which not touches uninitialized data members of Buddy.Which of my assumptions are correct, and which are wrong?
I use MSVS 2022 and C++14.
This is a MSVC bug.
What seems to happen is that MSVC interprets
Child() : Buddy { *this } {}
as
Child() : Buddy { static_cast<Buddy &>(*this) } {}
Which calls the copy constructor of Buddy. That initializes parent_ with itself, which causes undefined behavior (due to reading it before its lifetime starts, which starts when the initialization is finished).
The fix is to explicitly cast to Parent &:
Child() : Buddy { static_cast<Parent &>(*this) } {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With