gpt4 book ai didi

c++ - 为什么空基类也是成员变量时禁止空基优化?

转载 作者:行者123 更新时间:2023-12-01 09:20:29 28 4
gpt4 key购买 nike

Empty base optimization是很棒的。但是,它具有以下限制:

Empty base optimization is prohibited if one of the empty base classes is also the type or the base of the type of the first non-static data member, since the two base subobjects of the same type are required to have different addresses within the object representation of the most derived type.



要解释此限制,请考虑以下代码。 static_assert将失败。而更改任一 FooBar改为从 Base2 继承将避免错误:

#include <cstddef>

struct Base {};
struct Base2 {};

struct Foo : Base {};

struct Bar : Base {
Foo foo;
};

static_assert(offsetof(Bar,foo)==0,"Error!");

我完全理解这种行为。我不明白的是为什么存在这种特殊行为。很明显,添加它是有原因的,因为它是一个明确的添加,而不是一个疏忽。这样做的理由是什么?

特别是,为什么要要求两个基础子对象具有不同的地址?在上面, Bar是一种类型和 foo是该类型的成员变量。我不明白为什么 Bar 的基类与 foo 类型的基类有关, 或相反亦然。

事实上,如果有的话,我希望 &fooBar的地址相同包含它的实例——因为它需要在其他情况下 (1)。毕竟,我并没有对 virtual 做任何花哨的事情继承,不管基类都是空的,用 Base2编译表明在这种特殊情况下没有任何问题。

但显然这种推理在某种程度上是不正确的,并且在其他情况下需要这种限制。

假设答案应该是针对 C++11 或更新版本的(我目前使用的是 C++17)。

(1) 注意:EBO 在 C++11 中得到了升级,特别是在 StandardLayoutType 中成为强制性的s(尽管上面的 Bar 不是 StandardLayoutType )。

最佳答案

好吧,似乎我一直都错了,因为对于我的所有示例,都需要为基础对象存在一个 vtable,这将阻止空基础优化开始。我会让这些例子站着,因为我认为它们给出了一些有趣的例子,说明为什么唯一地址通常是一件好事。

更深入地研究这一点后,当第一个成员与空基类的类型相同时,没有技术原因禁用空基类优化。这只是当前 C++ 对象模型的一个属性。

但是在 C++20 中会有一个新属性 [[no_unique_address]]这告诉编译器非静态数据成员可能不需要唯一地址(从技术上讲,它可能与 [intro.object]/7 重叠)。

这意味着(强调我的)

The non-static data member can share the address of another non-static data member or that of a base class, [...]



因此,可以通过为第一个数据成员赋予属性 [[no_unique_address]] 来“重新激活”空基类优化。 .我添加了一个示例 here这显示了这个(以及我能想到的所有其他情况)是如何工作的。

通过这个问题的错误例子

由于似乎空类可能没有虚方法,让我添加第三个示例:

int stupid_method(Base *b) {
if( dynamic_cast<Foo*>(b) ) return 0;
if( dynamic_cast<Bar*>(b) ) return 1;
return 2;
}

Bar b;
stupid_method(&b); // Would expect 0
stupid_method(&b.foo); //Would expect 1

但是最后两个调用是相同的。

老例子 (可能不要回答这个问题,因为空类可能不包含虚拟方法,似乎)

在上面的代码(添加了虚拟析构函数)中考虑以下示例

void delBase(Base *b) {
delete b;
}

Bar *b = new Bar;
delBase(b); // One would expect this to be absolutely fine.
delBase(&b->foo); // Whoaa, we shouldn't delete a member variable.

但是编译器应该如何区分这两种情况呢?

也许不那么做作:

struct Base { 
virtual void hi() { std::cout << "Hello\n";}
};

struct Foo : Base {
void hi() override { std::cout << "Guten Tag\n";}
};

struct Bar : Base {
Foo foo;
};

Bar b;
b.hi() // Hello
b.foo.hi() // Guten Tag
Base *a = &b;
Base *z = &b.foo;
a->hi() // Hello
z->hi() // Guten Tag

但是如果我们有空基类优化,最后两个是一样的!

关于c++ - 为什么空基类也是成员变量时禁止空基优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59647419/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com