Поведение объясняется следующей цитатой из C ++ 17 Standard (11.6.1 Aggregates)
12 Брекеты могут быть исключены из списка инициализаторов следующим образом . Если список инициализаторов начинается с левой фигурной скобки, то последующий разделенный запятыми список предложений инициализаторов инициализирует элементы субагрегата; ошибочно иметь больше инициализаторов, чем элементов. Если, однако, список инициализатора для субагрегата не начинается с левой фигурной скобки, то для инициализации элементов субагрегата берется только достаточно предложений инициализатора из списка; любые оставшиеся предложения инициализатора оставляются для инициализации следующего элемента агрегата, элементом которого является текущий субагрегат.
Std :: array является агрегатом, который включает другой агрегат.
constexpr
std::array<my_enum_str_pair, static_cast<std::size_t>(my_enum::COUNT)>
my_enum_str_pairs = {
{ "first"sv, my_enum::first },
{ "second"sv, my_enum::second }, // error: excess elements in struct initializer
{ "third"sv, my_enum::third },
};
Таким образом, компилятор считает первую левую скобку в первом инициализаторе
{ "first"sv, my_enum::first },
^^^
В качестве инициализатора внутренней субагрегата (что обычно это массив) совокупности std::array
. После этого списка он встречает второй список
{ "second"sv, my_enum::second }
, но не находит другого подобъекта std::array
. Таким образом, компилятор выдает ошибку.
Если вы заключите эти списки в фигурные скобки
constexpr
std::array<my_enum_str_pair, static_cast<std::size_t>(my_enum::COUNT)>
my_enum_str_pairs = {
{
{ "first"sv, my_enum::first },
{ "second"sv, my_enum::second }, // error: excess elements in struct initializer
{ "third"sv, my_enum::third },
}
};
, то компилятор рассматривает весь подсписок как элементы внутренней субагрегата и использует этот субэлемент. list как инициализаторы конструктора класса std :: pair.
Во втором случае левые фигурные скобки
constexpr
std::array<my_enum_str_pair, static_cast<std::size_t>(my_enum::COUNT)>
my_enum_str_pairs = {
^^^^
my_enum_str_pair{ "first"sv, my_enum::first },
{ "second"sv, my_enum::second },
{ "third"sv, my_enum::third },
};
запускает список инициализаторов внутренней субагрегата, исключая еще одну фигурную скобку
Для большей ясности рассмотрим три примера одной и той же программы с разными подходами к инициализации.
Первая программа
#include <iostream>
struct Int
{
Int( int x ) : x( x ) {}
int x;
};
template <size_t N>
struct Array
{
Int a[N];
};
int main()
{
Array<3> a = { { 1 }, { 2 }, { 3 } };
return 0;
}
Компилятор выдаст ошибка, потому что первая открытая скобка перед 1
Array<3> a = { { 1 }, { 2 }, { 3 } };
^^^
рассматривается компилятором как список инициализатора внутреннего агрегата Int a[N]
.
Если добавить еще одну пару скобок типа
#include <iostream>
struct Int
{
Int( int x ) : x( x ) {}
int x;
};
template <size_t N>
struct Array
{
Int a[N];
};
int main()
{
Array<3> a = { { { 1 }, { 2 }, { 3 } } };
return 0;
}
Тогда в этом случае первые открытые скобки будут следующими
Array<3> a = { { { 1 }, { 2 }, { 3 } } };
^^^
, и элементы внутри этих скобок будут считаться инициализаторами Внутренний агрегат Int a[N]
.
В третьей программе
#include <iostream>
struct Int
{
Int( int x ) : x( x ) {}
int x;
};
template <size_t N>
struct Array
{
Int a[N];
};
int main()
{
Array<3> a = { Int{ 1 }, { 2 }, { 3 } };
return 0;
}
список инициализаторов не начинается с открытой фигурной скобки для внутреннего агрегата Int a [N]. Он начинается с выражения приведения
Int{ 1 }
Таким образом, все остальные элементы рассматриваются компилятором как инициализаторы внутреннего агрегата Int a [N]. Таким образом, согласно цитате, скобки могут быть исключены.