【问题标题】:Does casting an empty-base-class-optimized object to another type break strict aliasing?将空基类优化对象转换为另一种类型会破坏严格的别名吗?
【发布时间】:2020-01-09 09:38:07
【问题描述】:

考虑以下代码:

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

【问题讨论】:

  • 正常的向下转换必须至少使用 static_cast 完成,因为如果没有空基类优化可能会有偏移 - 我特别询问 reinterpret_cast。
  • 当然,感谢您的意见 :-)
  • 注意我们可能有offsetof(Foo, b1) == offsetof(Foo, y),没有必要0
  • 并且该属性允许但不强制编译器应用该优化。
  • 是的,它是实现定义的,GCC 和 clang 都选择将它们视为空基类(这是本文的建议 - 请参阅此处的 Q.2:open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0840r2.html) .

标签: c++ language-lawyer strict-aliasing c++20


【解决方案1】:

您不需要reinterpret_cast 或“空基优化”来从基类转换为派生类。这完全可以通过static_cast 实现,只要指针指向派生类类型的对象(并且不使用virtual 继承)。这是标准的 C++98 东西。

确实,如果你想成为技术人员,reinterpret_cast 可能不适合这个。 reinterpret_casting between two pointers works as if by doing a cast to a void* between them。所以地址是一样的。是的,如果基类和派生类的地址恰好相同,则地址将被保留。但就 C++ 对象模型而言,这还不够好。

现在,指向基类的指针可以与派生类相互转换,但仅当两个类都是标准布局时。这是因为这样的指针是pointer-interconvertible

但空基优化并不需要标准布局类型。尽管需要标准布局的类型才能使用 EBO,但类型可以从 EBO 中受益,同时违反标准布局类型的规则。 offsetof 当然只适用于标准布局类型。

因此,就您提出的问题文本而言,这不能保证有效。

这将我们带到no_unique_address。指针互转换规则明确说明了标准布局类型的基类/派生类。它有一个关于标准布局类型的第一个非静态数据成员 (NSDM) 的特定声明(它与包含对象的指针可互转换)。但是没有提到所有其他 NSDM,因此它们不是指针可互转换的。

因此,reinterpret_cast 还不够好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-29
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多