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

ehxuflar  于 2023-06-07  发布在  其他
关注(0)|答案(5)|浏览(189)

在C++14标准中,std::array的初始化可以使用单括号(参见http://en.cppreference.com/w/cpp/container/array):
然而,这对于std::pairstd::array不起作用。
为什么这些工作:

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}};
pdsfdshx

pdsfdshx1#

这看起来是一个解析上的歧义,有点类似于著名的most vexing parse。我怀疑现在的情况是:
如果你写

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

编译器有两种解释语法的方法:
1.执行全括号初始化(这意味着最外面的大括号引用std::array的聚合初始化,而第一个最里面的大括号初始化std::array的内部成员表示,这是一个真实的的C数组)。这将无法编译,因为1随后无法初始化std::pair<int, int>(所有大括号都用完了)。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}};

会像聚合初始化一样编译得很好。
1.(你的意思是这样的。)你执行了一个大括号省略的初始化,最里面的大括号因此是用于单个对的聚合初始化,而内部数组表示的大括号被省略了。请注意,即使没有这种二义性,正如在rustyx's answer中正确指出的那样,括号省略的规则也不适用,因为std::pair不是聚合类型,所以程序仍然是病态的。
编译器将首选选项1。通过提供额外的大括号,可以执行全大括号初始化并消除任何语法歧义。

f0brbegy

f0brbegy2#

C++14大括号省略规则仅适用于子聚合初始化。
例如这样的工作:

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

在这里,集合的集合可以被列表初始化,而不需要额外的括号。
但是std::pair不是一个聚合(它有构造函数),因此该规则不适用。
这意味着如果没有大括号省略规则,std::array本身就是一个内部有数组的聚合,需要一组额外的大括号才能 * 列表初始化 *。请记住,类模板array是这样实现的:

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

要在不使用大括号省略规则的情况下 list-initialize 它,您需要一组额外的大括号来访问elems成员。

zzoitvuj

zzoitvuj3#

如果没有双大括号,该语句就是二义性的。请考虑以下代码:

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

如果第一个定义中没有双括号,编译器将把{ {1,2} }视为array<int, 2>的 * 标量初始化列表 *。你需要声明一个显式的 * 嵌套的花括号初始化列表 *,以便编译器识别内部列表也是 * 聚合初始化的 *(与标量初始化),使得它可以构造std::pair的数组。

uqcuzwp8

uqcuzwp84#

理论上std::array应该用聚合初始化来初始化。实际上是这样的:

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

是这个的语法糖:

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

正如你所看到的,在第一个中,我似乎用值初始化数组,但它实际上是用花括号初始化列表的聚合初始化。在第二种情况下,这是显而易见的。所以这只是开始。
好吧,那么为什么这不起作用呢?

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

gwo2fgha

gwo2fgha5#

我来猜猜。
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<??> )

相关问题