为什么不能用两层list-initializers初始化2D std :: array?

Pet*_*ano 9 c++ list-initialization stdarray

有人可以帮助我理解为什么我的编译器不能/不推断这个?(使用g ++ 7.3)

不起作用:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {{0,0},{0,0}};
}
Run Code Online (Sandbox Code Playgroud)

工作良好:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {std::array<double,2>{0,0},{0,0}};
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是,这也失败了:

#include <array>
std::array<std::array<double,2>,2> f() {
 return std::array<std::array<double,2>,2>{{0,0},{0,0}};
}
Run Code Online (Sandbox Code Playgroud)

@ 1201ProgramAlarm指出添加另一组花括号有效:

#include <array>
std::array<std::array<double,2>,2> f() {
 return {{{0,0},{0,0}}};
}
Run Code Online (Sandbox Code Playgroud)

它使用聚合初始化,因为std::array没有用于brace-init-list的构造函数.那没关系,但那么为什么/如何运作?

std::array<double,2> x{1,2};
Run Code Online (Sandbox Code Playgroud)

为什么它处理这种情况但不处理嵌套的情况?

xsk*_*xzr 7

容器std::array等效地是一个包含C数组的结构(实现可能没有std::array以这种方式实现,但它应该保证语义是相同的),所以它应该由两层大括号初始化,即

#include <array>
std::array<std::array<double,2>,2> f() {
   return {{{{0,0}},{{0,0}}}};
} 
Run Code Online (Sandbox Code Playgroud)

当然,初始化列表中的大括号可以像我们通常对2D数组所做的那样省略:

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

...但是在elision之后以el括号开头的初始化列表不应该以左括号开始.换句话说,如果初始化列表以左括号开头,编译器将不会考虑此初始化列表已经省略了最外侧括号的可能性.

在初始化程序中{{0,0},{0,0}},子初始化程序{0,0},{0,0}以左括号开头,因此它用于初始化C数组本身.但是,列表中有两个子句,而只有一个C阵列,发生错误.

在初始化程序中{std::array<double,2>{0,0},{0,0}},子初始化程序std::array<double,2>{0,0},{0,0}不以左括号开头,因此它可用于初始化C数组的元素,这是正常的(递归地,{0,0}可以初始化a,std::array<double,2>因为子初始化程序0,0没有开始用左支撑).


一个建议:有了这个括号的省略规则,你可以省略所有内括号,就像我们通常对2D数组所做的那样:

#include <array>
std::array<std::array<double,2>,2> f() {
   return {0,0,0,0};
} 
Run Code Online (Sandbox Code Playgroud)