为什么我不需要 3 级大括号来初始化 3 级数组?

Căt*_*rbu 8 c++ struct nested uniform-initialization

我遇到了这个例子

struct sct
{
    int t[2];
};

struct str
{
    sct t[2];
};

int main()
{
    str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }; //Who does this work?

    cout << t[1].t[0].t[1] << t[0].t[1].t[0];     

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这编译并运行良好。它给出了输出34

我预计初始化的语法是:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };
Run Code Online (Sandbox Code Playgroud)

代替

 { {0, 2, 4, 6}, {1, 3, 5, 7} };
Run Code Online (Sandbox Code Playgroud)

但这给了:

In function 'int main()':
error: too many initializers for 'str'
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么吗?

这是一张图片来说明我的看法: 在此处输入图片说明

在初始化嵌套结构时我应该如何思考?

Dmi*_*rov 7

这看起来是一个简单的错字,但情况非常复杂,可以逐步解决。

首先让我展示似乎有效的解决方案:

int main()
{
    str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
    cout << t[1].t[0].t[1] << t[0].t[1].t[0] << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

所以我们有一个str包含sct.

让我们从后者开始。你sct用这样的东西初始化一个数组:

sct x[2] = { {0, 1}, {2, 3} };
Run Code Online (Sandbox Code Playgroud)

现在,对于str您可以使用的单个实例

str y = { { {0, 2}, {4, 6} } };
Run Code Online (Sandbox Code Playgroud)

剩下的str t[2]就是str在大括号内排列两个初始化表达式的副本:

str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
Run Code Online (Sandbox Code Playgroud)

编辑:在第一次阅读时,我误解了这个问题。帖子更新后,问题变得很明显,为什么可以去掉两对大括号,但只去掉一对会导致语法错误。

要了解解析器如何解释代码,您可能需要查看解析树。您可以使用-fdump-tree-...选项在解析器的几个阶段制作 gcc 转储树。这里-fdump-tree-original可能有用。

为了避免额外的混淆,让我们确保结构的元素具有不同的名称:

struct sct
{
    int a[2];
};

struct str
{
    sct b[2];
};
Run Code Online (Sandbox Code Playgroud)

这是我从 GCC 7.5 得到的输出

>>>> CODE:
str t[2] = { { 0, 2, 4, 6 }, { 1, 3, 5, 7 } };
>>>> tree enabled by -tree-original
struct str t[2] = {{.b={{.a={0, 2}}, {.a={4, 6}}}}, {.b={{.a={1, 3}}, {.a={5, 7}}}}};
Run Code Online (Sandbox Code Playgroud)

您可以看到编译器在每个结构的初始化表达式和每个命名字段的初始化表达式周围添加了隐式括号。

现在考虑编译失败的表达式:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };
Run Code Online (Sandbox Code Playgroud)

这个表达式的树的上层是

/*Step 1: */ struct str t[2] = { {.b={0, 2}, {4, 6} }, {.b={1, 3}, {5, 7} } };
Run Code Online (Sandbox Code Playgroud)

但是由于 b 是 的数组sct,我们尝试使用{0,2}获取来初始化它

sct b[2] = {0, 2};
Run Code Online (Sandbox Code Playgroud)

这扩展到

struct sct b[2] = {{.a={0, 2} }};
Run Code Online (Sandbox Code Playgroud)

这是有效的 C++,因为数组的第一个元素是显式初始化的,而第二个元素是用零隐式初始化的。

有了这些知识,我们得到以下树

/*Step 2: */ struct str t[2] = { {.b={{.a={0, 2} }}, {4, 6} }, {.b={{.a={1, 3} }}, {5, 7} } };
Run Code Online (Sandbox Code Playgroud)

现在我们剩下以下内容:

 struct str z = { { { {0,2} }, { {0,0} } }, {4, 6} };
Run Code Online (Sandbox Code Playgroud)

编译器理所当然地抱怨:

 error: too many initializers for ‘str’
Run Code Online (Sandbox Code Playgroud)

作为最后检查,请考虑以下声明

 struct sxc
 {
     sct b[2];
     int c[2];
 }

 struct sxc z = { {0,2} , {4, 6} };
Run Code Online (Sandbox Code Playgroud)

这将编译并产生以下结构:

 { .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }
Run Code Online (Sandbox Code Playgroud)