The first class will be used for private inheritance in order to ensure the exact same layout. This should make casting safe.
#include <iostream>
#include <string>
struct data_base
{
data_base( int i, std::string&& s ) noexcept
: i_{ i }
, s_{ std::move( s ) }
{}
int i_;
std::string s_;
};
In this trivial example, I print the int
data member first followed by the std::string
data member for instances of data<true>
.
template<bool = true>
struct data : private data_base // inherits
{
data( int i, std::string&& s ) noexcept
: data_base( i, std::move( s ) )
{}
void print()
{
std::cout << "data<true> - " << i_ << s_ << '\n';
}
};
However, the data<false>
prints the std::string
data member first, followed by the int
data member.
template<>
struct data<false> : private data_base
{
void print()
{
std::cout << "data<false> - " << s_ << i_ << '\n';
}
};
Example:
int main()
{
data<true> d{ 5, "abc" };
d.print();
( ( data<false>& )d ).print();
}
Demo: http://coliru.stacked-crooked.com/a/8b1262afe23dc0a2
As the demo shows, even with the -fstrict-aliasing
flag on, there's no warnings.
Now, since they have the same layout, I thought that I could just cast between the two types in order to get a different kind of static polymorphism; without the cost of a virtual function call.
Is this usage safe or am I triggering undefined behaviour?
It's more or less what's described here, the so called boost mutant idiom.
There it is said that (emphasis mine):
Boost mutant idiom makes use of
reinterpret_cast
and depends heavily on assumption that the memory layouts of two different structures with identical data members (types and order) are interchangeable. Although the C++ standard does not guarantee this property, virtually all the compilers satisfy it. Moreover, the mutant idiom is standard if only POD types are used.
Note: that page is pretty outdated, I don't know if the most recent revisions changed something about the guarantees above mentioned.
From [expr.reinterpret.cast]/11 in the language spec, you can cast a reference from one type to another (if you can cast a pointer to one to the other).
With your class layouts, both types have a common base class that holds all the data. The two derived types do not add any data members, nor do they add any virtual functions, so the object layout for both classes will be the same.
So the usage is safe if you use reinterpret_cast
.
In this case, this is similar to casting to a reference the base class, then casting that reference to the other derived class.
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