将空基 class 优化的对象转换为另一种类型会破坏严格的别名吗?
Does casting an empty-base-class-optimized object to another type break strict aliasing?
考虑以下代码:
struct Base1 { };
struct Base2 { };
struct Foo : Base1, Base2 {
int x;
};
Foo f;
Base1* b1 = &f; // ok, standard upcasting
Base2* b2 = &f; // ok, standard upcasting
// check that the empty base class optimization applies
assert(((void*)b1) == ((void*)&f));
assert(((void*)b2) == ((void*)&f));
assert(((void*)b1) == ((void*)b2));
从 b1
或 b2
访问指向 foo 的指针是否合法?例如
std::cout << reinterpret_cast<Foo*>(b1)->x
<< reinterpret_cast<Foo*>(b2)->x;
如果是,是否同样适用于 C++20 的属性 no_unique_address
,假设一个实现选择像对待空基数 类 一样对待 [[no_unique_address]]
成员?
例如GCC 和 Clang,但不是 MSVC,当前验证以下内容:
struct Foo {
int x;
[[no_unique_address]] Base1 b1;
char y;
[[no_unique_address]] Base2 b2;
};
static_assert(offsetof(Foo, b1) == 0));
static_assert(offsetof(Foo, b2) == 0));
所以 b1 和 b2 都有它们父对象的地址,可以在这里验证:https://gcc.godbolt.org/z/NF9ACy
您不需要 reinterpret_cast
或 "empty base optimization" 即可从基础 class 转换为派生 class。这完全可以通过 static_cast
实现,只要指针指向派生的 class 类型的对象(并且不使用 virtual
继承)。这是标准的 C++98 东西。
的确,如果你想成为技术人员,reinterpret_cast
可能不适合这个。 reinterpret_cast
ing between two pointers works as if by doing a cast to a void*
between them。所以地址将是相同的。是的,如果基址和派生 classes 的地址恰好相同,该地址将被保留。但就 C++ 对象模型而言,这还不够好。
现在,指向基 class 的指针可以与派生的 class 相互转换,但是 仅当 两个 class 都是标准布局。这是因为这样的指针是 pointer-interconvertible.
但是空基优化不需要标准布局类型。虽然使用 EBO 需要 标准布局类型,但违反标准布局类型规则的类型可以从 EBO 中受益。 offsetof
当然只适用于标准布局类型。
就您所问问题的文本而言,不能保证有效。
这将我们带到 no_unique_address
。指针相互转换的规则明确说明了标准布局类型的 base/derived classes。并且它有一个关于标准布局类型的第一个非静态数据成员(NSDM)的特定声明(它可以与包含的对象进行指针互换)。但所有其他 NSDM 均未提及,因此它们不是指针可相互转换的。
因此,reinterpret_cast
还不够好。
考虑以下代码:
struct Base1 { };
struct Base2 { };
struct Foo : Base1, Base2 {
int x;
};
Foo f;
Base1* b1 = &f; // ok, standard upcasting
Base2* b2 = &f; // ok, standard upcasting
// check that the empty base class optimization applies
assert(((void*)b1) == ((void*)&f));
assert(((void*)b2) == ((void*)&f));
assert(((void*)b1) == ((void*)b2));
从 b1
或 b2
访问指向 foo 的指针是否合法?例如
std::cout << reinterpret_cast<Foo*>(b1)->x
<< reinterpret_cast<Foo*>(b2)->x;
如果是,是否同样适用于 C++20 的属性 no_unique_address
,假设一个实现选择像对待空基数 类 一样对待 [[no_unique_address]]
成员?
例如GCC 和 Clang,但不是 MSVC,当前验证以下内容:
struct Foo {
int x;
[[no_unique_address]] Base1 b1;
char y;
[[no_unique_address]] Base2 b2;
};
static_assert(offsetof(Foo, b1) == 0));
static_assert(offsetof(Foo, b2) == 0));
所以 b1 和 b2 都有它们父对象的地址,可以在这里验证:https://gcc.godbolt.org/z/NF9ACy
您不需要 reinterpret_cast
或 "empty base optimization" 即可从基础 class 转换为派生 class。这完全可以通过 static_cast
实现,只要指针指向派生的 class 类型的对象(并且不使用 virtual
继承)。这是标准的 C++98 东西。
的确,如果你想成为技术人员,reinterpret_cast
可能不适合这个。 reinterpret_cast
ing between two pointers works as if by doing a cast to a void*
between them。所以地址将是相同的。是的,如果基址和派生 classes 的地址恰好相同,该地址将被保留。但就 C++ 对象模型而言,这还不够好。
现在,指向基 class 的指针可以与派生的 class 相互转换,但是 仅当 两个 class 都是标准布局。这是因为这样的指针是 pointer-interconvertible.
但是空基优化不需要标准布局类型。虽然使用 EBO 需要 标准布局类型,但违反标准布局类型规则的类型可以从 EBO 中受益。 offsetof
当然只适用于标准布局类型。
就您所问问题的文本而言,不能保证有效。
这将我们带到 no_unique_address
。指针相互转换的规则明确说明了标准布局类型的 base/derived classes。并且它有一个关于标准布局类型的第一个非静态数据成员(NSDM)的特定声明(它可以与包含的对象进行指针互换)。但所有其他 NSDM 均未提及,因此它们不是指针可相互转换的。
因此,reinterpret_cast
还不够好。