多个CRTP基的对齐类
Alignment of multiple CRTP base classes
在 CRTP 中,基础对象可以 。
在多重继承的情况下也是如此吗?第二个基址及以后的基址可能位于与派生对象不同的地址。考虑例如:
#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 的未定义行为分析器says this is undefined behavior.
clang 的未定义行为分析器 detects no problem.
标准说的对吗?
更新:
gcc的bug目前已经在trunk上修复
是的,这个定义好了,你的代码就OK了。多重继承是铸造指针与原始指针不同的罕见情况。
如果你去源:
[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. ...
这意味着您使用的转换是有效的,并且只要您不丢弃任何 cv 限定符,使用指针也同样有效。
据我所知,这是清理程序中的一个错误,在必须重定向时无法强制转换引用。
首先,这与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;
输出:
0x7ffc7eeab4d0
0x7ffc7eeab4d1
0x7ffc7eeab4d0
这是正确的,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;
突然间你得到
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
^
意味着输出再次正确,但消毒剂在投回时错误地没有重定向引用。
在 CRTP 中,基础对象可以
在多重继承的情况下也是如此吗?第二个基址及以后的基址可能位于与派生对象不同的地址。考虑例如:
#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 的未定义行为分析器says this is undefined behavior.
clang 的未定义行为分析器 detects no problem.
标准说的对吗?
更新:
gcc的bug目前已经在trunk上修复
是的,这个定义好了,你的代码就OK了。多重继承是铸造指针与原始指针不同的罕见情况。
如果你去源:
[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. ...
这意味着您使用的转换是有效的,并且只要您不丢弃任何 cv 限定符,使用指针也同样有效。
据我所知,这是清理程序中的一个错误,在必须重定向时无法强制转换引用。
首先,这与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;
输出:
0x7ffc7eeab4d0
0x7ffc7eeab4d1
0x7ffc7eeab4d0
这是正确的,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;
突然间你得到
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
^
意味着输出再次正确,但消毒剂在投回时错误地没有重定向引用。