Reputation: 1591
I'm interested in having tuples with "holes" in them. These holes are an empty struct. All holes will have the same type, called Empty
here.
For illustration purposes, let
struct DByte {
std::array<std::byte, 2> data;
};
struct Empty {
};
. As I expected,
sizeof(std::tuple<DByte, Empty, Empty>) = 2
. However,
sizeof(std::tuple<Empty, DByte, Empty>) = 3
sizeof(std::tuple<Empty, Empty, DByte>) = 3
and I can't understand why these last two types aren't of size 2 (on a platform with sizeof(std::byte) == 1 && alignof(std::byte) == 1
).
This demonstration also prints the memory layout of these types under Clang and GCC.
Why aren't all these tuples the same size with the following layout:
0 1 2
| DByte |
| Empty | Empty |
?
Upvotes: 2
Views: 173
Reputation: 217398
Why aren't all these tuples the same size with the following layout:
They might have, it is just a optimization miss.
You might see the same miss for regular classes:
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;
};
Notice that msvc gives same size for your different tuples Demo. (always 4, so without empty class optimization).
Layout of tuple is not specified. So all those are possible.
Upvotes: 0
Reputation: 238361
Why aren't all these tuples the same size with the following layout:
Because the implementations of std::tuple
do not re-order their sub objects based on the types of the contained objects (although they are not required to not do so). They use one order that depends on the order of parameters, but not on the types of those parameters.
Normally, all sub objects are required by the standard to have a unique address, and there may not be overlap between sub objects of a standard layout class. There is an exception1 for empty base classes, which are not required to have a unique address, and that special rule is what some std::tuple
implementations take advantage of to make empty sub objects use no memory. But it only works when the order of the sub objects is correct for the exception to apply.
1 C++20 adds another exception, but the standard libraries implemented std::tuple
in C++11 and cannot practically change their layout without breaking ABI, so that is not important to the implementation of std::tuple
. The C++20 feature wouldn't improve the optimisation without re-ordering the sub objects either.
Upvotes: 3