std::array
is required by the standard to be well-defined even when the size is zero, so that std::array<T, 0>
is well-defined even though int[0]
is not allowed (though GCC implements an extension for it).
However, on GCC, Clang, and MSVC, std::array<T, 0>
is not treated as if it is a zero-sized struct, i.e. it does not act like it has trailing padding, and so [[no_unique_address]]
and the empty base optimization does not work with it. For example:
#include <array>
struct Tester {
[[no_unique_address]] std::array<char, 0> arr;
char c;
};
struct Tester2 : std::array<char, 0> {
char c;
};
// true on GCC, Clang, and MSVC at least:
static_assert(sizeof(Tester) == 2);
static_assert(sizeof(Tester2) == 2);
This is surprising, since it's obviously implementable as an empty struct. Is this something mandated by the standard?
Note: I've looked into the code for libstdc++, and it works like this because std::array<char, 0>
contains an instance of an empty struct that is not [[no_unique_address]]
and not using empty base class optimization, i.e. it looks something like this:
template <typename T>
struct array<T, 0> {
struct {} empty; // no [[no_unique_address]]!
};
But this doesn't doesn't explain why it was designed this way. Even though [[no_unique_address]]
wasn't a thing until C++20, the empty base class optimization is used liberally in other parts of the standard library, so it's definitely something that could be done if desired.
Edit: My question got marked as a duplicate of Are C++ compilers actually compliant with zero-size array SFINAE rule?. This is not true at all; my question is about the library feature std::array
, which is well-defined by the standard even when the size is 0. The linked question is about the language feature of arrays.
Could it be that the empty
struct
is related to the double-brace initialisation "issue" in C++11?std::array<T,N> myArray = {{ ... }};
Before N3526 there were scenarios where braces needed to be doubled to list initialise a
std::array
. Ifstd::array<T,0>
had been implemented as a simple emptystruct
, initialisation via double-braces wouldn't have compiled.So maybe to ensure that all
std::array
instantiations could be initialised with the same syntax they added an empty inner class?