使用声明中的元素类型隐式初始化 std::array

Initialize std::array using element type from the declaration implicitly

在下面的 code 中,我正在尝试初始化数组(原始 C 经典和 std::array),尽量减少元素类型的使用,即 S:

#include <array>

struct S { unsigned u; int i; };

auto std_array = std::array<S, 3>{
    S{0U, 0},
    S{1U, 1},
    S{2U, 2}
};

S raw_array[] = {
    {0U, 0},
    {1U, 1},
    {2U, 2}
}; 

/*  // compile error: excess elements in struct initializer
std::array<S,3> std_array_no_type = {
    {0U, 0},
    {1U, 1},
    {2U, 2}
}; 
*/

std::array<S,3> std_array_one_type_only = {
    S{0U, 0},
    {1U, 1},
    {2U, 2}
}; 

int main() {}

使用 raw_array 我只能指定一次 S。但是用 std::array 做同样的尝试是行不通的(见注释 std_array_no_type)。我必须为每个 or 指定 S 类型(这也是问题的一个有趣部分)仅为初始化列表中的第一个元素(参见 std_array_one_type_only)。

那么,有没有一种方法可以只使用类型 S 定义一个已初始化的 std::array 对象?如果不是,根据标准的哪个条款?为什么单个显式类型 S 允许 std_array_one_type_only 被编译?

关于第一个问题,

So, is there a way to define an initialized std::array object using the type S only once?

你可以再添加一对{},因为std::array实际上包含一个底层数组

std::array<S,3> std_array_one_type_only = {{{0U, 0}, {1U, 1}, {2U, 2}}}; 
//                                        ^                           ^ for std::array
//                                         ^                         ^  for underlying array
//                                          ^     ^                     for the 1st element of underlying array
//                                                   ^     ^            for the 2nd element of underlying array
//                                                            ^     ^   for the 3rd element of underlying array

关于第二个问题,

And why single explicit type S allows std_array_one_type_only to be compiled?

aggregate initialization中,嵌套初始化列表的大括号可以省略,

If the aggregate initialization uses copy- (until C++14)list-initialization syntax (T a = {args..} or T a {args..} (since C++14)), the braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object.

给定

std::array<S,3> std_array_no_type = {{0U, 0}, {1U, 1}, {2U, 2}}; 
//                                  ^                         ^  for std::array
//                                   ^     ^                     for underlying array

第一个 {0U, 0} 将被尝试用于初始化整个底层数组,然后导致像 excess elements in initializer 这样的错误,因为 std::array 不包含任何其他子对象。

给出

std::array<S,3> std_array_no_type = {S{0U, 0}, {1U, 1}, {2U, 2}}; 
//                                  ^                          ^ for std::array
//                                    ^     ^                    for the 1st element of underlying array
//                                             ^     ^           for the 2nd element of underlying array
//                                                      ^     ^  for the 3rd element of underlying array

S{0U, 0}不能用来初始化底层数组(不满足聚合初始化的风格),那么它会用来初始化底层数组的第一个元素,即应用上面的大括号省略规则,然后使用下面的 {1U, 1}{2U, 2} 来初始化基础数组的以下成员。