问题描述
我对有“洞”的元组感兴趣。这些孔是一个空结构。所有孔都具有相同的类型,此处称为 Empty
。
为了说明目的,让
struct DByte {
std::array<std::byte,2> data;
};
struct Empty {
};
。正如我所料,
sizeof(std::tuple<DByte,Empty,Empty>) = 2
。然而,
sizeof(std::tuple<Empty,DByte,Empty>) = 3
sizeof(std::tuple<Empty,DByte>) = 3
我不明白为什么最后两种类型的大小不是 2(在带有 sizeof(std::byte) == 1 && alignof(std::byte) == 1
的平台上)。
This demonstration 还会打印这些类型在 Clang 和 GCC 下的内存布局。
为什么所有这些元组的大小与以下布局不同:
0 1 2
| DByte |
| Empty | Empty |
?
解决方法
为什么所有这些元组的大小与以下布局不同:
因为 std::tuple
的实现不会根据所包含对象的类型对其子对象重新排序(尽管它们不需要这样做)。它们使用一种取决于参数顺序的顺序,但不取决于这些参数的类型。
正常情况下,标准要求所有的子对象都有一个唯一的地址,一个标准布局类的子对象之间不能有重叠。对于空基类有一个例外1,它不需要具有唯一的地址,并且该特殊规则是某些 std::tuple
实现利用使空子对象使用 no记忆。但只有当子对象的顺序正确才能应用异常时,它才有效。
1 C++20 增加了另一个例外,但标准库在 C++11 中实现了 std::tuple
,并且在不破坏 ABI 的情况下实际上不能改变它们的布局,所以这对std::tuple
的实现。如果不重新排序子对象,C++20 特性也不会改进优化。
为什么所有这些元组的大小与以下布局不同:
他们可能有,这只是优化失误。
您可能会在常规课程中看到同样的错过:
struct A
{
[[no_unique_address]]DByte m;
[[no_unique_address]]Empty e1;
[[no_unique_address]]Empty e2;
};
struct B
{
[[no_unique_address]]Empty e1;
[[no_unique_address]]DByte m;
[[no_unique_address]]Empty e2;
};
struct C
{
[[no_unique_address]]Empty e1;
[[no_unique_address]]Empty e2;
[[no_unique_address]]DByte m;
};
请注意,msvc 为不同的元组 Demo 提供相同的大小。 (总是 4,所以没有空类优化)。
未指定元组的布局。所以所有这些都是可能的。