std::array 结构初始化列表语法
std::array of structures initializater list syntax
考虑以下 C++ 代码:
struct My_Struct
{
int a;
int b;
};
现在我要声明这些结构的常量std::array:
选项A:
const std::array<My_Struct,2> my_array =
{
{1,2},
{2,3}
};
选项 B:
const std::array<My_Struct,2> my_array =
{
My_Struct(1,2),
My_Struct(2,3)
};
问:为什么选项A编译失败,只有选项B编译通过?初始化时嵌套花括号有什么问题 std::array?
在我看来,编译器应该具有成功编译选项 A 的所有必要信息,但它会产生“太多的初始化值”错误。
同时,嵌套花括号在初始化普通数组时非常有效:
const My_Struct my_array[] =
{
{1,2},
{2,3}
};
编译器为 MSVC 17.2,C++20 模式开启。
我可以接受选项 B,但我想了解在 C++ 知识方面我在选项 A 中到底有什么不明白的。
std:array
是一个包含My_Struct的C风格数组的容器。所以你需要一个额外的花括号:
const std::array<My_Struct,2> my_array = {
{
{1,2},
{2,3},
}
};
std::array
是一个包含实际数组的 class。它看起来像这样:
template <typename T, size_t N>
struct array
{
T _unspecified_name[N];
// Member functions.
};
请注意,std::array
没有构造函数或私有数据成员,因此它是一个聚合。
当您 brace-initialize a std::array
时,您正在对 class 进行聚合初始化,其中一个成员本身就是一个聚合。通常这需要两套牙套:
// Braces for std::array
// │ │
// ▼ ▼
std::array<int, 2> arr = { {1, 2} };
// ▲ ▲
// │ │
// Braces for int[2] member
C++ 有 brace-elision 规则来改善这种情况。您可以省略内部大括号集,初始化器将“传递”到本身是聚合的基础成员对象。在这种情况下,它是 std::array
的 int[2]
成员。
这是您问题的根源。当您使用大括号初始化 My_Struct
的数组时,编译器认为您打算将内部大括号集初始化 std::array
的 My_Struct[]
成员而不是其中的 My_Struct
。由于有两个初始值设定项,而它只期望一个初始值设定项,因此会抛出错误:
// Initializer for std::array
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { {1, 2}, {2, 3} };
// ▲ ▲ ▲ ▲
// │ │ │ │
// Initializer for unnamed My_Struct[2] │ │
// What is this initializer for? I only have one member to initialize
为避免这种情况,您只需添加另一组大括号:
// ┌ Initializer for std::array ┐
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { { {1, 2}, {2, 3} } };
// ▲ ▲ ▲ ▲ ▲ ▲
// | │ │ │ │ │
// | |Initializers│ │
// | For array elements │
// | |
// Initializer for unnamed My_Struct[2]
选项 A 的问题 是您缺少一组对应于内部 C 样式数组 T[N]
的大括号 {}
。
This container is an aggregate type with the same semantics as a struct holding a C-style array T[N]
as its only non-static data member.
这反过来意味着,我们将需要 class 类型 std::array
本身的外括号然后是 C 样式 T[N]
数组的内括号。
现在让我们将其应用到您的示例中并编写正确的语法。在您的示例中 T[N]
与 My_Struct[2]
.
相同
//-----------------------------------------v--------------v-------------> needed for inner C style array T[N] which is the same as My_Struct[2]
const std::array<My_Struct,2> my_array = { { {1,2}, {2,3} } };
//-------------------------------------------^---^--^---^---------------> needed for the object type `My_Struct`
//---------------------------------------^------------------^-----------> needed for class type std::array itself
考虑以下 C++ 代码:
struct My_Struct
{
int a;
int b;
};
现在我要声明这些结构的常量std::array:
选项A:
const std::array<My_Struct,2> my_array =
{
{1,2},
{2,3}
};
选项 B:
const std::array<My_Struct,2> my_array =
{
My_Struct(1,2),
My_Struct(2,3)
};
问:为什么选项A编译失败,只有选项B编译通过?初始化时嵌套花括号有什么问题 std::array?
在我看来,编译器应该具有成功编译选项 A 的所有必要信息,但它会产生“太多的初始化值”错误。
同时,嵌套花括号在初始化普通数组时非常有效:
const My_Struct my_array[] =
{
{1,2},
{2,3}
};
编译器为 MSVC 17.2,C++20 模式开启。
我可以接受选项 B,但我想了解在 C++ 知识方面我在选项 A 中到底有什么不明白的。
std:array
是一个包含My_Struct的C风格数组的容器。所以你需要一个额外的花括号:
const std::array<My_Struct,2> my_array = {
{
{1,2},
{2,3},
}
};
std::array
是一个包含实际数组的 class。它看起来像这样:
template <typename T, size_t N>
struct array
{
T _unspecified_name[N];
// Member functions.
};
请注意,std::array
没有构造函数或私有数据成员,因此它是一个聚合。
当您 brace-initialize a std::array
时,您正在对 class 进行聚合初始化,其中一个成员本身就是一个聚合。通常这需要两套牙套:
// Braces for std::array
// │ │
// ▼ ▼
std::array<int, 2> arr = { {1, 2} };
// ▲ ▲
// │ │
// Braces for int[2] member
C++ 有 brace-elision 规则来改善这种情况。您可以省略内部大括号集,初始化器将“传递”到本身是聚合的基础成员对象。在这种情况下,它是 std::array
的 int[2]
成员。
这是您问题的根源。当您使用大括号初始化 My_Struct
的数组时,编译器认为您打算将内部大括号集初始化 std::array
的 My_Struct[]
成员而不是其中的 My_Struct
。由于有两个初始值设定项,而它只期望一个初始值设定项,因此会抛出错误:
// Initializer for std::array
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { {1, 2}, {2, 3} };
// ▲ ▲ ▲ ▲
// │ │ │ │
// Initializer for unnamed My_Struct[2] │ │
// What is this initializer for? I only have one member to initialize
为避免这种情况,您只需添加另一组大括号:
// ┌ Initializer for std::array ┐
// │ │
// ▼ ▼
std::array<My_Struct, 2> my_array = { { {1, 2}, {2, 3} } };
// ▲ ▲ ▲ ▲ ▲ ▲
// | │ │ │ │ │
// | |Initializers│ │
// | For array elements │
// | |
// Initializer for unnamed My_Struct[2]
选项 A 的问题 是您缺少一组对应于内部 C 样式数组 T[N]
的大括号 {}
。
This container is an aggregate type with the same semantics as a struct holding a C-style array
T[N]
as its only non-static data member.
这反过来意味着,我们将需要 class 类型 std::array
本身的外括号然后是 C 样式 T[N]
数组的内括号。
现在让我们将其应用到您的示例中并编写正确的语法。在您的示例中 T[N]
与 My_Struct[2]
.
//-----------------------------------------v--------------v-------------> needed for inner C style array T[N] which is the same as My_Struct[2]
const std::array<My_Struct,2> my_array = { { {1,2}, {2,3} } };
//-------------------------------------------^---^--^---^---------------> needed for the object type `My_Struct`
//---------------------------------------^------------------^-----------> needed for class type std::array itself