Bernardo Sulzbach
Bernardo Sulzbach

Reputation: 1591

Why aren't all these tuples the same size

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

Answers (2)

Jarod42
Jarod42

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;
};

Demo

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

eerorika
eerorika

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

Related Questions