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::arrayint[2] 成员。

这是您问题的根源。当您使用大括号初始化 My_Struct 的数组时,编译器认为您打算将内部大括号集初始化 std::arrayMy_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] 的大括号 {}

来自std::array's documentation

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