&引用;没有与第一个非静态数据成员类型相同的基类; 我在C.P.ST.C++上没有得到答复。
我只是想引用一下我的帖子,稍作修改&引用;没有与第一个非静态数据成员类型相同的基类; 我在C.P.ST.C++上没有得到答复。,c++,struct,c++11,C++,Struct,C++11,我只是想引用一下我的帖子,稍作修改 标准布局类9/6的最后一个要求是必要的还是有用的 提供脚注说明: 这样可以确保两个子对象 具有相同的类类型,并且 属于同一个最派生的对象 不在同一地址分配 (5.10) 单独来看,脚注是不正确的。两个带有 公共基类可以同时生成基类的两个实例 同一地址 struct A {}; struct B : A {}; struct C : A {}; struct D : B, C {}; D d; static_cast<A*>(static_cas
标准布局类9/6的最后一个要求是必要的还是有用的 提供脚注说明: 这样可以确保两个子对象 具有相同的类类型,并且 属于同一个最派生的对象 不在同一地址分配 (5.10) 单独来看,脚注是不正确的。两个带有 公共基类可以同时生成基类的两个实例 同一地址
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
D d;
static_cast<A*>(static_cast<B*>(&d))
== static_cast<A*>(static_cast<C*>(&d)); // allowed per 1.8/5
struct A{};
结构B:A{};
结构C:A{};
结构D:B,C{};
D;
静态浇铸
这表明多重继承可能会带来麻烦和需要
不允许在标准布局类中使用,以确保:;
然而,这最终是允许的,鉴于此,要求
没有道理
参考1.8/5-6:
5除非是位字段(9.6),否则
大多数派生对象应具有
非零尺寸,且应占据一个或多个
更多字节的存储空间。基类
子对象的大小可能为零。一
可复制的或不可复制的对象
应采用标准布局类型(3.9)
占用连续的存储字节
6除非对象是位字段或
大小为零的基类子对象
那个对象的地址就是地址
它占用的第一个字节的。两个
两者都不是的不同对象
位字段或基类子对象
零尺寸应具有明显的
地址
(脚注)根据“仿佛”规则,允许实现在同一机器地址存储两个对象,或者如果程序无法观察到差异,则完全不存储对象
补充说明:
10.1/8在5.10中提到了相同的神秘内容,但它也只是一个信息注释
[注:…基类子对象的大小可以为零(第9条);但是,不得在同一地址(5.10)分配具有相同类类型且属于同一最派生对象的两个子对象。-结束注]
GCC似乎保证为相同类型的空基子对象提供唯一的地址。这似乎足以保证给定类型的对象由地址唯一标识。这将超出C++对象模型(1.8)的保证。当然这是一个好主意,但标准似乎并不要求这样做。同样,平台ABI可以将这种保证扩展到第一个成员别名为空基的类。该语言规定了ABI的最低要求;一个ABI可以添加一个语言特性,其他ABI也可以这样做,标准追赶的过程很容易出错
我的问题是给定的需求是否在标准的上下文中完成了任何事情,而不是它是否与其他ABI保证一起对用户有用。有证据表明,这样一个独特的地址保证是有意的,而且只是偶然遗漏,这也会使要求更有意义
总结一下答案(或者我的结论):
该要求在理论上并不能保证任何东西,因为它可以确保给定类型的所有对象都有不同的地址。当空基类子对象的地址与另一个对象(另一个基类或成员)冲突时,编译器可以简单地在结构中为其指定任意位置。由于标准布局规则仅描述数据成员的位置(可能是继承的),因此空基的位置仍然未指定,并且可能在类似的标准布局类之间不兼容。(据我所知,非空基地的位置尚未确定,因此不清楚在这种情况下“第一成员”是什么意思,但在任何情况下都必须保持一致。)
实际上,该需求允许实现继续使用现有的ABI,只要它们包括空的基类优化。当违反要求时,现有编译器可能会禁用EBO,以避免基地址与第一个成员的地址重合。如果标准没有以这种方式限制程序,库和程序将不得不用更新的C++0x编译器重新编译…这不值得
具有公共基类的两个空基类必须在同一地址生成基类的两个实例
struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
D d;
static_cast<A*>(static_cast<B*>(&d))
== static_cast<A*>(static_cast<C*>(&d)); // allowed per 1.8/5
我不这么认为。事实上,快速检查我的g++副本表明我有两个不同的a对象地址。也就是说,你上面的代码不是真的
事实上,按照类的编写方式,我们必须有两个A对象。如果两个对象共享同一地址,则它们在任何意义上都不是两个不同的对象。所以,要求对象的实例存在不同的地址
假设A的定义如下:
class A
{
static std::set<A*> instances;
A() { instances.insert(this); }
~A() { instances.remove(this); }
}
A类
{
静态std::设置实例;
A(){instances.insert(this);}
~A(){instances.remove(this);}
}
如果允许A的两个副本共享一个地址,则此代码将无法正常工作。我相信,正是在这种情况下,我们才决定对A的不同副本使用不同的addresess。当然,正是这种情况的复杂性使我避免了多重继承。如果你将等式表达式放在assert()
中,你会发现它失败了。A子对象位于不同的位置。这是正确的行为,无需指定virtual
:
struct B : virtual A {};
struct C : virtual A {};
使用virtual
,根据第二条规则,D已经不是标准布局类。这就是C++ 98、03和'0x.中的情况。
编辑以反映评论:
再次编辑:没关系,这还不够
标准布局类定义的要点是指定可以与其他语言一起使用的内容。让我们用C作为例子
struct X{
B base;
B b;
int i;
};
struct X{
B b;
int i;
};
#include <assert.h>
#include <iostream>
struct E {};
struct D: E { E x ; };
int main()
{
D d;
std::cerr << "&d: " << (void*)(&d) << "\n";
std::cerr << "&d.x: " << (void*)(&(d.x)) << "\n";
std::cerr << "(E*)&d: " << (void*)(E*)(&d) << "\n";
assert(reinterpret_cast<E *>(&d) == &d.x); //standard-layout requirement
}
&d: 0x7fffc76c9420
&d.x: 0x7fffc76c9421
(E*)&d: 0x7fffc76c9420
testLayout: testLayout.cpp:19: int main(): Assertion `reinterpret_cast(&d) == &d.x' failed.
Aborted