In CRTP, the base object can return a reference to the derived object via static cast.
Is this also true in the case of multiple inheritance? The second base and beyond might be at addresses different from the derived object. Consider for instance:
#include <iostream>
#include <string_view>
template<typename Derived>
struct Base1
{
char c1;
};
template<typename Derived>
struct Base2
{
char c2;
auto& get2() const
{
return static_cast<const Derived&>(*this); // <-- OK?
}
};
struct X : public Base1<X>, public Base2<X>
{
X(std::string_view d) : data{d} {}
std::string_view data;
};
int main()
{
auto x = X{"cheesecake"};
std::cout << x.get2().data << std::endl;
}
gcc's undefined behavior analyzer says this is undefined behavior.
clang's undefined behavior analyzer detects no problem.
Does the standard say which of them is right?
Update:
The bug in gcc has been fixed on trunk by now.
Yes, this is defined, your code is OK. Multiple inheritance is the rare case in which the casted pointer differs from the original.
If you go to the Source:
[expr.static.cast.11] A prvalue of type “pointer to cv1 B”, where B is a class type, can be converted to a prvalue of type “pointer to cv2 D”, where D is a complete class derived from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. ....
[expr.static.cast.2] An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a class derived from B, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. ...
Meaning that the cast you have used is valid and the same would work have you used pointers as long as you do not cast away any cv qualifiers.
To my best knowledge, this is a bug in the sanitizer which has trouble with casting references when they have to be redirected.
First, this has nothing to do with CRTP. The following does exactly the same, CRTP does that just automatically for us.
Base2<X>* base = &x;
const X* orig = static_cast<const X*>(base);
std::cout << &x << std::endl;
std::cout << base << std::endl;
std::cout << orig << std::endl;
Output:
0x7ffc7eeab4d0
0x7ffc7eeab4d1
0x7ffc7eeab4d0
Which is correct and gcc's sanitizer does not complain about anything.
But if you change pointers to references:
X x{"cheesecake"};
Base2<X>& base = x;
const X& orig = static_cast<const X&>(base);//Line 36
std::cout << &x << std::endl;
std::cout << &base << std::endl;
std::cout << &orig << std::endl;
Suddenly you get
0x7ffdbf87cf50
0x7ffdbf87cf51
0x7ffdbf87cf50
Program stderr
example.cpp:36:14: runtime error: reference binding to misaligned address 0x7ffdbf87cf51 for type 'const struct X', which requires 8 byte alignment
0x7ffdbf87cf51: note: pointer points here
00 00 00 60 cf 87 bf fd 7f 00 00 0a 00 00 00 00 00 00 00 66 20 40 00 00 00 00 00 6d 19 40 00 00
^
Meaning that the output is again correct, but the sanitizer incorrectly does not redirect the reference when casting back.
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