If all of the members of std::tuple are of standard layout types, is that std::tuple itself standard layout? The presence of a user-defined copy-constr
One reason std::tuple cannot be of standard layout, as any classes with members and base classes with members, is that the standard allows for space optimization when deriving even non-empty base classes. For example:
#include
#include
class X
{
uint64_t a;
uint32_t b;
};
class Y
{
uint16_t c;
};
class XY : public X, public Y
{
uint16_t d;
};
int main() {
printf("sizeof(X) is %zu\n", sizeof(X));
printf("sizeof(Y) is %zu\n", sizeof(Y));
printf("sizeof(XY) is %zu\n", sizeof(XY));
}
Outputs:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 16
The above shows that the standard allows for class trailing padding to be used for the derived class members. Class XY has two extra uint16_t members, yet its size equals to the size of base class X.
In other words, class XY layout is the same as that of another class that has no base classes and all the members of XY ordered by address, e.g. struct XY2 { uint64_t a; uint32_t b; uint16_t c; uint16_t d; };.
What makes it non-standard layout is that the size of a derived class is not a function of sizes of base classes and derived class members.
Note that the size of a struct/class is a multiple of the alignment of one of its members with the largest alignment requirement. So that an array of objects is suitably aligned for such a member. For built-in types normally sizeof(T) == alignof(T). Hence sizeof(X) is a multiple of sizeof(uint64_t).
I am not sure whether the standard requires special treatment for struct, but with g++-5.1.1 if class is replaced with struct the above code yields different output:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 24
In other words, the trailing padding space optimization is not used when struct is involved (did not test for exact conditions).