代码之家  ›  专栏  ›  技术社区  ›  Chiel

为什么在C++ 14中,成对数组的初始化仍然需要双括号?

  •  61
  • Chiel  · 技术社区  · 6 年前

    用C++ 14标准,初始化一个 std::array 可配单撑(参见 http://en.cppreference.com/w/cpp/container/array ):

    但是,这对于 STD::阵列 属于 std::pair .

    为什么这样做:

    std::pair<int, int> p { 1, 2 };
    std::array<int, 3> a {1, 2, 3};
    

    但这是不是 工作:

    std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};
    

    当这又起作用的时候?

    std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};
    

    此外,为了完成此操作,一个好的旧数组的初始化确实可以使用单个大括号。

    std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};
    
    5 回复  |  直到 6 年前
        1
  •  30
  •   Chiel    6 年前

    这似乎是一种语法分析的笨拙,有点类似于著名的 most vexing parse . 我怀疑发生了什么事:

    如果你写信

    std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};
    

    编译器有两种解释语法的方法:

    1. 执行完全大括号初始化(意味着最外面的大括号是指 std::array ,而第一个内部成员初始化的内部成员表示形式 STD::阵列 这是一个真正的C阵列)。这将无法编译,因为 std::pair<int, int> 随后不能由初始化 1 (所有的支架都用完了)。clang将给出一个编译器错误,精确地指示:

      error: no viable conversion from 'int' to 'std::pair<int, int>'
       std::array<std::pair<int, int>, 3> a{{1, 11}, {2, 22}, {3, 33}};
                                                ^
      

      注意,如果没有要初始化的内部成员聚合(即

      std::pair<int, int> b[3] = {{1, 11}, {2, 22}, {3, 33}};
      

      将编译为聚合初始化。

    2. (您的意思是。)您执行大括号省略的初始化,因此最里面的大括号用于单个对的聚合初始化,而内部数组表示形式的大括号则被省略。请注意,即使没有这种歧义,正如 rustyx's answer ,括号省略规则不适用于 std::pair 不是聚合类型,因此程序的格式仍然不正确。

    编译器将首选选项1。通过提供额外的大括号,可以执行完全大括号初始化并消除任何语法上的歧义。

        2
  •  14
  •   rustyx    6 年前

    C++ 14 brace elision rule 仅适用于子聚合初始化。

    例如,类似这样的工作:

    std::array<std::array<int, 3>, 3> a{1, 11, 2, 22, 3, 33};
    

    在这里,聚合的聚合可以在没有额外大括号的情况下进行列表初始化。

    但是 std::pair 不是 aggregate (它有构造函数),因此规则不适用。

    也就是说,如果没有大括号省略规则, std::array ,本身是一个包含数组的聚合,需要一组额外的大括号才能 列表已初始化 . 记住类模板 array 实施方式如下:

    template<typename T, std::size_t N> 
    struct array {
      T elems[N];
    };
    

    列表初始化 如果没有大括号省略规则,则需要一组额外的大括号才能 elems 成员。

        3
  •  8
  •   andreee    6 年前

    如果没有双大括号,这个语句就是不明确的。考虑以下代码:

        std::array<std::pair<int, int>, 1> a = {{ {1, 2} }};
        std::array<int, 2> b = { {1, 2} };
    

    { {1,2} } 作为一个 标量初始化列表 对于 array<int, 2> . 您需要声明 嵌套大括号初始化列表 为了让编译器认识到内部列表 聚合已初始化 (相对于标量初始化),这样它可以构造 std::pair .

        4
  •  0
  •   bartop    6 年前

    理论上 std::array 应使用聚合初始化进行初始化。所以事实上:

    std::array<int, 3> a {1, 2, 3};
    

    是一种语法糖:

    std::array<int, 3> a {{1, 2, 3}};
    

    如您所见,在第一个示例中,我似乎用值初始化数组,但实际上是用带括号的in it list进行聚合初始化。在第二种情况下,这一天很明显。所以这只是开始。

    好吧,那为什么这不起作用呢?

    std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};
    

    好吧,简单地说-编译器无法区分您使用什么类型的语法来初始化数组。 {1, 11} 可以解释为初始值设定项列表并使用第一个版本,也可以解释为一对并与第二个版本一起使用。

    此代码:

    std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};.
    

    消除歧义。

    资料来源: http://en.cppreference.com/w/cpp/language/aggregate_initialization

        5
  •  -2
  •   jwm    6 年前

    我来猜猜。
    的初始值设定项列表 std::array<T,n> 应该是 T (或者可以构造为 T )所以你可以

    std::array<std::pair<int,int>,3> b { std::pair{1,11}, std::pair{2,22}, std::pair{3,33} };
    

    但那是冗长乏味的。为了得到转换到 std::pair<int,int> 你需要,你需要提供一个初始值设定项列表,所以

    std::array<std::pair<int,int>,3> b {
        { // element 1
          { // initialize from:
            { 1,11 } // std::initializer_list
           }
         },
      ...
    };
    

    我不能再为这个辩护了,但请注意 std::vector<T, Allocator>::vector( std::initializer_list<T>, const Allocator& alloc=Allocator()) 定义,但 std::array<T,n>::array( std::initializer_list<T> ) 不是。两者都不是 std::pair<U,T>::pair( std::initializer_list<??> ) 定义。