将空基 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));

b1b2 访问指向 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_casting 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 还不够好。