Rum*_*rak 6 c++ multiple-inheritance crtp undefined-behavior language-lawyer
在 CRTP 中,基础对象可以通过 static cast 返回对派生对象的引用。
在多重继承的情况下也是如此吗?第二个基址及以后的地址可能与派生对象不同。考虑例如:
#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;
}
Run Code Online (Sandbox Code Playgroud)
gcc 的未定义行为分析器说这是未定义行为。
clang 的未定义行为分析器检测到没有问题。
标准是否说明了他们中的哪一个是正确的?
更新:
gcc 中的错误现已在主干上修复。
是的,这是定义的,您的代码没问题。多重继承是一种罕见的情况,其中强制转换的指针与原始指针不同。
如果你去源码:
[expr.static.cast.11]类型为“指向 cv1 B 的指针”的纯右值,其中 B 是类类型,可以转换为类型为“指向 cv2 D 的指针”的纯右值,其中 D 是从B,如果 cv2 与 cv1 具有相同的 cv 限定,或比 cv1 更高的 cv 限定。....
[expr.static.cast.2]类型为“cv1 B”的左值,其中 B 是类类型,可以转换为类型“对 cv2 D 的引用”,其中 D 是从 B 派生的类,如果 cv2 是与 cv1 相同或更高的 cv 资格。...
这意味着您使用的强制转换是有效的,并且只要您不丢弃任何 cv 限定符,使用指针的情况也是如此。
据我所知,这是 sanitizer 中的一个错误,当必须重定向引用时,它在转换引用时遇到问题。
首先,这与 CRTP 无关。以下完全相同,CRTP 会自动为我们执行此操作。
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;
Run Code Online (Sandbox Code Playgroud)
输出:
0x7ffc7eeab4d0
0x7ffc7eeab4d1
0x7ffc7eeab4d0
Run Code Online (Sandbox Code Playgroud)
这是正确的,gcc 的消毒剂不会抱怨任何事情。
但是,如果您更改指向引用的指针:
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;
Run Code Online (Sandbox Code Playgroud)
突然你得到
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
^
Run Code Online (Sandbox Code Playgroud)
这意味着输出再次正确,但消毒剂在回滚时不正确地重定向引用。