In the new C++20 standard, cpprefrence says:
a temporary bound to a reference in a reference element of an aggregate initialized using direct-initialization syntax (parentheses) as opposed to list-initialization syntax (braces) exists until the end of the full expression containing the initializer. Example:
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference
NOTE: I am using GCC as this is the only compiler which supports this feature. Having read this, and knowing that references are polymorphic, I decided to create a simple class:
template <typename Base>
struct Polymorphic {
Base&& obj;
};
So that the following code works:
struct Base {
inline virtual void print() const {
std::cout << "Base !\n";
}
};
struct Derived: public Base {
inline virtual void print() const {
std::cout << "Derived !" << x << "\n";
}
Derived() : x(5) {}
int x;
};
int main() {
Polymorphic<Base> base{Base()};
base.obj.print(); // Base !
Polymorphic<Base> base2{Derived()};
base2.obj.print(); // Derived 5!
}
The problem I encountered is when changing the value of my polymorphic object (not the value of obj, but the value of a polymorphic object itself). Since I can't reassign to r-value references, I tried to do the following using placement new:
#include <new>
int main() {
Polymorphic<Base> base{Base()};
new (&base) Polymorphic<Base>{Derived()};
std::launder(&base)-> obj.print(); // Segmentation fault... Why is that?
return 0;
}
I believe I have a segmentation fault because Polymorphic<Base>
has a reference sub object. However, I am using std::launder
- isn't that supposed to make it work? Or is this a bug in GCC? If std::launder
does not work, how do I tell the compiler not to cache the references?
On a side note, please do not tell me "your code is stupid", "use unique pointers instead"... I know how normal polymorphism works; I asked this question to deepen my understanding of placement new and std::launder :)