为什么用结构初始化数组需要指定结构名称
Why does intializing array with structs requires specifying the struct name
为什么这段代码会产生编译时错误?
#include <array>
#include <cstdint>
#include <string_view>
using namespace std::string_view_literals;
enum class my_enum : std::size_t {
first = 0,
second,
third,
COUNT,
};
struct my_enum_str_pair {
std::string_view str;
my_enum command;
};
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 },
};
但是如果我们把它改成
...
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 },
};
编译正常吗?
您缺少一对括号。
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 }
}};
C++ 17 标准(11.6.1 聚合)中的以下引述解释了该行为
12 Braces can be elided in an initializer-list as follows. If the
initializer-list begins with a left brace, then the succeeding
comma-separated list of initializer-clauses initializes the elements
of a subaggregate; it is erroneous for there to be more
initializer-clauses than elements. If, however, the initializer-list
for a subaggregate does not begin with a left brace, then only enough
initializer-clauses from the list are taken to initialize the elements
of the subaggregate; any remaining initializer-clauses are left to
initialize the next element of the aggregate of which the current
subaggregate is an element.
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 },
}
};
然后编译器将所有子列表视为内部子聚合的元素,并将此子列表用作 class 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] 的初始值设定项。所以根据引用,大括号可能会被省略。
为什么这段代码会产生编译时错误?
#include <array>
#include <cstdint>
#include <string_view>
using namespace std::string_view_literals;
enum class my_enum : std::size_t {
first = 0,
second,
third,
COUNT,
};
struct my_enum_str_pair {
std::string_view str;
my_enum command;
};
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 },
};
但是如果我们把它改成
...
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 },
};
编译正常吗?
您缺少一对括号。
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 }
}};
C++ 17 标准(11.6.1 聚合)中的以下引述解释了该行为
12 Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the elements of a subaggregate; it is erroneous for there to be more initializer-clauses than elements. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the elements of the subaggregate; any remaining initializer-clauses are left to initialize the next element of the aggregate of which the current subaggregate is an element.
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 },
}
};
然后编译器将所有子列表视为内部子聚合的元素,并将此子列表用作 class 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] 的初始值设定项。所以根据引用,大括号可能会被省略。