为什么花括号包围的初始化列表不能用于std::array?

4

我想用一个对象列表来初始化向量或数组。这对于向量有效,但不适用于数组:

struct Widget
{
    string name;
    vector<int> list;
};

struct Object
{
    string name;
    vector<int> list;
    Object(string _name, vector<int> _list) : name(_name), list(_list) { }
};

int main()
{
    const vector<Widget> vw = {
        {"vw1", {1,2,3}},
        {"vw2", {1,2,3}} };
    const array<Widget,2> aw = {
        {"aw1", {1,2,3}},
        {"aw2", {1,2,3}} };
    const vector<Object> vo = {
        {"vo1", {1,2,3}},
        {"vo2", {1,2,3}} };
    const array<Object,2> ao = {
        {"ao1", {1,2,3}},
        {"ao2", {1,2,3}} };
    return 0;
}

来自clang的错误:

widget.cpp:36:9: error: excess elements in struct initializer
        {"aw2", {1,2,3}} };
        ^~~~~~~~~~~~~~~~
widget.cpp:41:10: error: no viable conversion from 'const char [4]' to 'Object'
        {"ao1", {1,2,3}},
         ^~~~~
widget.cpp:41:17: error: no matching constructor for initialization of 'Object'
        {"ao1", {1,2,3}},
                ^~~~~~~

什么是向量和数组之间的区别,是什么导致数组类型不支持这种语法?

我不是100%确定,但我认为这是聚合初始化和括号包含的初始化器列表之间的奇怪交互。可以按照下面的答案描述解决它,但我不确定问题的具体原因是什么。更仔细地检查错误消息表明,该数组“吸收”了一个额外的大括号,无论如何,缺乏更好的术语;请注意,它认为“ao1”和“{1,2,3}”是两个不同的“Object”,而不是同一“Object”的初始化器列表的一部分。 - Justin Time - Reinstate Monica
2个回答

4

这是一个有效的解决方案 - 对于数组,您需要使用双括号。

int main()
{
    const vector<Widget> vw = {
        {"vw1", {1,2,3}},
        {"vw2", {1,2,3}} };
    const array<Widget,2> aw = {{
        {"aw1", {1,2,3}},
        {"aw2", {1,2,3}} }};
    const vector<Object> vo = {
        {"vo1", {1,2,3}},
        {"vo2", {1,2,3}} };
    const array<Object,2> ao = {{
        {"ao1", {1,2,3}},
        {"ao2", {1,2,3}} }};
    return 0;
}

为什么?

http://en.cppreference.com/w/cpp/container/array

std::array是一个封装了固定大小数组的容器。该容器是一个聚合类型,具有与结构体相同的语义,其唯一的非静态数据成员为C风格数组T[N]。与C风格数组不同的是,它不会自动衰减为T*。作为聚合类型,它可以用最多N个可转换为T的初始化程序进行聚合初始化:std::array a = {1,2,3}。

理论实现(在现实中更为复杂)

template <typename T, size_t size> 
struct array  
{
  T data[size]; 
}

首先,第一个大括号用于聚合初始化数组对象本身;第二个大括号用于聚合初始化内部的"传统C风格"数组。


很有意思。这是很多大括号...但是所写的东西说得通。另外链接的问题/答案也帮了很大忙。谢谢! - Jan Müller

0
将您的示例添加另一对大括号将使它们能够正确地工作:
int main()
{
    const std::array<Widget,2> aw = {
        {
            {"aw1", {1, 2, 3}},
            {"aw2", {1, 2, 3}}
        }
    };

    const std::array<Object, 2> ao = {
        {
            {"ao1", {1, 2, 3} },
            {"ao2", {1, 2, 3} }
        }
    };

    cout << aw[0].name << " " << aw[1].list[1] << endl;
    cout << ao[0].name << " " << ao[1].list[1] << endl;

    return 0;
}

将会输出:

aw1 2
ao1 2

额外的一对括号是必要的,因为 std::array 没有自己的构造函数,而只是使用聚合初始化而不是列表初始化。
在实现方面,一个 std::array 由一个内部项目组成,即真正的基础数组,它包含 N 个元素。因此,我们需要额外的括号,以便用 一个 元素(这是一个数组,在您的示例中有两个元素)初始化 std::array

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接