std :: array<>的初始化 [英] Initialisation of std::array<>

查看:136
本文介绍了std :: array<>的初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

#include <array>

struct A
{
    int a;
    int b;
};

static std::array<A, 4> x1 =
{
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
        { 7, 8 }
};

static std::array<A, 4> x2 =
{
    {
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
        { 7, 8 }
    }
};

static std::array<A, 4> x3 =
{
       A{ 1, 2 },
       A{ 3, 4 },
       A{ 5, 6 },
       A{ 7, 8 }
};

static std::array<A, 4> x4 =
{
       A{ 1, 2 },
        { 3, 4 },
        { 5, 6 },
        { 7, 8 }
};

使用gcc编译:

$ gcc -c --std=c++11 array.cpp
array.cpp:15:1: error: too many initializers for ‘std::array<A, 4ul>’
 };
 ^
$

NB1:注释掉第一个初始化语句后,代码将正确编译.
NB2:将所有初始化都转换为构造函数调用会产生相同的结果.
NB3:MSVC2015的行为相同.

NB1: Commenting out the first initialisation statement, the code compiles without error.
NB2: Converting all the initialisation to constructor calls yields the same results.
NB3: MSVC2015 behaves the same.

我可以看到为什么第一次初始化无法编译,为什么第二次和第三次都可以. (例如,参见 C ++ 11:正确的std :: array初始化吗?.)

I can see why the first initialisation fails to compile, and why the second and third are OK. (e.g. See C++11: Correct std::array initialization?.)

我的问题是:为什么最终初始化要精确编译?

My question is: Why exactly does the final initialisation compile?

推荐答案

简短版本:以{开头的初始化子句停止大括号删除.在使用{1,2}的第一个示例中就是这种情况,但是在使用A{1,2}的第三个也不是第四个示例中就是这种情况.括号删除消耗了接下来的N个初始化器子句(其中N取决于要初始化的聚合),这就是为什么只有N个第一个初始化器子句不能以{开头的原因.

Short version: An initializer-clause that starts with { stops brace-elision. This is the case in the first example with {1,2}, but not in the third nor fourth which use A{1,2}. Brace-elision consumes the next N initializer-clauses (where N is dependent on the aggregate to be initialized), which is why only the first initializer-clause of the N must not begin with {.

在我所知道的C ++标准库的所有实现中,std::array是一个包含C样式数组的结构.也就是说,您有一个包含 sub-aggregate 的聚合,很像

In all implementations of the C++ Standard Library I know of, std::array is a struct which contains a C-style array. That is, you have an aggregate which contains a sub-aggregate, much like

template<typename T, std::size_t N>
struct array
{
    T __arr[N]; // don't access this directly!
};

braced-init-list 初始化std::array时,因此必须初始化包含数组的成员.因此,在这些实现上,显式形式为:

When initializing a std::array from a braced-init-list, you'll therefore have to initialize the members of the contained array. Therefore, on those implementations, the explicit form is:

std::array<A, 4> x = {{ {1,2}, {3,4}, {5,6}, {7,8} }};

最外面的大括号表示std::array结构;第二组大括号是指嵌套的C样式数组.

The outermost set of braces refers to the std::array struct; the second set of braces refers to the nested C-style array.

C ++允许在初始化嵌套聚合时在聚合初始化中省略某些花括号.例如:

C++ allows in aggregate initialization to omit certain braces when initializing nested aggregates. For example:

struct outer {
    struct inner {
        int i;
    };
    inner x;
};

outer e = { { 42 } };  // explicit braces
outer o = {   42   };  // with brace-elision

规则如下(使用N4527之后的草案,即C ++ 14之后的版本,但C ++ 11仍然存在与此相关的缺陷):

The rules are as follows (using a post-N4527 draft, which is post-C++14, but C++11 contained a defect related to this anyway):

可以将

括号隐藏在 initializer-list 中,如下所示.如果 initializer-list 以左大括号开头,然后是左大括号 以逗号分隔的 initializer-clauses 列表初始化成员 子集合;有更多的东西是错误的 initializer-clauses 而不是成员.但是,如果 initializer-list 因为子集合不是以左大括号开头,而是 从列表中提取足够的 initializer-clauses 来初始化 子集合的成员;其余的初始化器子句是 向左初始化集合中的下一个成员 当前的子集合是成员.

Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializer-clauses than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member.

将其应用于第一个std::array-示例:

Applying this to the first std::array-example:

static std::array<A, 4> x1 =
{
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
        { 7, 8 }
};

这解释如下:

static std::array<A, 4> x1 =
{        // x1 {
  {      //   __arr {
    1,   //     __arr[0]
    2    //     __arr[1]
         //     __arr[2] = {}
         //     __arr[3] = {}
  }      //   }

  {3,4}, //   ??
  {5,6}, //   ??
  ...
};       // }

第一个{被用作std::array结构的初始化程序.然后,将初始化程序子句 {1,2}, {3,4}等用作std::array子聚合的初始化程序.请注意,std::array仅具有单个子聚合__arr.由于第一个 initializer-clause {1,2}{开头,因此不会出现 brace-elision异常,并且编译器会尝试初始化嵌套的A __arr[4] {1,2}组成的数组.其余的初始化程序子句 {3,4}, {5,6}等均未引用std::array的任何子集合,因此是非法的.

The first { is taken as the initializer of the std::array struct. The initializer-clauses {1,2}, {3,4} etc. then are taken as the initializers of the subaggregates of std::array. Note that std::array only has a single subaggregate __arr. Since the first initializer-clause {1,2} begins with a {, the brace-elision exception does not occur, and the compiler tries to initialize the nested A __arr[4] array with {1,2}. The remaining initializer-clauses {3,4}, {5,6} etc. do not refer to any subaggregate of std::array and are therefore illegal.

在第三个和第四个示例中,std::array 的子集合的第一个 initializer-clause 并非以{ 开头,因此大括号省略例外已应用:

In the third and fourth example, the first initializer-clause for the subaggregate of std::array does not begin with a {, therefore the brace elision exception is applied:

static std::array<A, 4> x4 =
{
       A{ 1, 2 }, // does not begin with {
        { 3, 4 },
        { 5, 6 },
        { 7, 8 }
};

因此,其解释如下:

static std::array<A, 4> x4 =
  {             // x4 {
                //   __arr {       -- brace elided
    A{ 1, 2 },  //     __arr[0]
    { 3, 4 },   //     __arr[1]
    { 5, 6 },   //     __arr[2]
    { 7, 8 }    //     __arr[3]
                //   }             -- brace elided
  };            // }

因此,A{1,2}使所有四个 initializer-clauses 被消耗,以初始化嵌套的C样式数组.如果添加另一个初始化程序:

Hence, the A{1,2} causes all four initializer-clauses to be consumed to initialize the nested C-style array. If you add another initializer:

static std::array<A, 4> x4 =
{
       A{ 1, 2 }, // does not begin with {
        { 3, 4 },
        { 5, 6 },
        { 7, 8 },
       X
};

,则此X将用于初始化std::array的下一个子聚合.例如

then this X would be used to initialize the next subaggregate of std::array. E.g.

struct outer {
    struct inner {
        int a;
        int b;
    };

    inner i;
    int c;
};

outer o =
  {        // o {
           //   i {
    1,     //     a
    2,     //     b
           //   }
    3      //   c
  };       // }

括号删除将使用接下来的N个初始化程序子句,其中N是通过(子)集合初始化所需的初始化程序数来定义的.因此,仅重要的是,这N个初始化子句中的第一个是否以{开头.

Brace-elision consumes the next N initializer-clauses, where N is defined via the number of initializers required for the (sub)aggregate to be initialized. Therefore, it only matters whether or not the first of those N initializer-clauses starts with a {.

与OP更相似:

struct inner {
    int a;
    int b;
};

struct outer {
    struct middle {
        inner i;
    };

    middle m;
    int c;
};

outer o =
  {              // o {
                 //   m {
    inner{1,2},  //     i
                 //   }
    3            //   c
  };             // }

请注意,括号删除是递归应用的;我们甚至可以写出令人困惑的

Note that brace-elision applies recursively; we can even write the confusing

outer o =
  {        // o {
           //   m {
           //     i {
    1,     //       a
    2,     //       b
           //     }
           //   }
    3      //   c
  };       // }

在这里我们省略了o.mo.m.i的两个括号.前两个初始化器子句用于初始化o.m.i,其余两个子句用于初始化o.c.一旦我们在1,2周围插入一对大括号,它将被解释为与o.m对应的一对大括号:

Where we omit both the braces for o.m and o.m.i. The first two initializer-clauses are consumed to initialize o.m.i, the remaining one initializes o.c. Once we insert a pair of braces around 1,2, it is interpreted as the pair of braces corresponding to o.m:

outer o =
  {        // o {
    {      //   m {
           //     i {
      1,   //       a
      2,   //       b
           //     }
    }      //   }
    3      //   c
  };       // }

在这里,o.m的初始化程序的确以{开头,因此大括号省略不适用. o.m.i的初始值设定项是1,它不是以{开头,因此对o.m.i应用大括号删除,并且消耗了两个初始值设定项12.

Here, the initializer for o.m does start with a {, hence brace-elision does not apply. The initializer for o.m.i is 1, which does not start with a {, hence brace-elision is applied for o.m.i and the two initializers 1 and 2 are consumed.

这篇关于std :: array&lt;&gt;的初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆