【问题标题】:Subarray assignment: initialization with "{...}" expected for aggregate object error子数组分配:聚合对象错误预期使用“{...}”进行初始化
【发布时间】:2019-10-15 11:54:44
【问题描述】:

第 1 行的错误有什么好的解释?

/* Line 0 */    int foo[10][10];
/* Line 1 */    int foo_alias_compile_error[] = foo[0];
/* Line 2 */    int foo_alias_also_errors[10] = foo[0];
/* Line 3 */    int * foo_alias_works = foo[0];

第 2 行的错误并没有真正困扰我,因为我不需要能够重复自己并重新声明数组的大小。但是,第 1 行的错误(聚合对象需要用“{...}”初始化)让我感到困惑。我知道int foo_alias_compile_error[] 可能是“聚合对象”。我只是不明白为什么语言设置为不起作用。我理解为什么第 3 行有效,但它有点不具代表性——它是一个数组,所以我宁愿将它作为一个数组进行自我记录。

【问题讨论】:

  • 您可能更喜欢使用std::array<std::array<int, 10>, 10>。它可以按照您的意愿进行自我记录,并按照您的预期行事,就像其他值类型一样。
  • 啊,这是一个好点。不幸的是,这是在 GPU 上的,所以我没有任何 STL 可用。
  • 请注意,第 3 行提供了一个指向 foo[0] 的指针,这不是您在第 1 行中想要的。看起来您打算复制该数组。如果是这样的话,第 3 行的行为会大不相同。
  • 您应该使用您使用的任何着色器语言来标记问题。这不是 C++ 问题,即使许多着色器语言看起来很像 C++。
  • @ragerdl 如果你只想引用一个数组,你需要int (&bar)[10] = foo[0];。这里bar是对int[10]的引用,初始化为数组foo[0]

标签: c++ arrays initialization


【解决方案1】:

人们可能希望将第 1 行视为为 foo 的第 0 个条目创建引用。然而,这不是 C++ 的工作方式。相反,赋值运算符 (=) 正在调用复制操作。因此,C++ 将第 1 行解释为将foo[0] 的元素复制到一个名为foo_alias_compile_error 的新数组中的指令。

这不是本意——人们想要的是参考,而不是副本。所以,C++ 碰巧因为一个不相关的原因导致了一个错误并从自己那里救了一个,这很好。

@FrançoisAndrieux 提出了一个可行的解决方案。这是一个更完整的示例,显示可以使用int (&foo_reference)[10] = foo[0]; 进行引用(而不是副本)。

int foo[10][10];
for (int i = 0; i < 10; ++i) {
    for (int j = 0; j < 10; ++j) {
        foo[i][j] = i + j * 10;
    }
}
int (&foo_reference)[10] = foo[0];

for (int j = 0; j < 10; ++j) {
    foo_reference[j] = 100 + j;
}

for (int i = 0; i < 10; ++i) {
    printf("foo   [0][%i] is %i\n", i, foo[0][i]);
    printf("foo_refer[%i] is %i\n", i, foo_reference[i]);
}

输出片段

foo   [0][0] is 100
foo_alias[0] is 100
foo   [0][1] is 101
foo_alias[1] is 101
foo   [0][2] is 102
foo_alias[2] is 102
foo   [0][3] is 103
foo_alias[3] is 103

旁注

值得一提的是,将数组作为参数的函数会隐式地将其数组参数转换为指针(如第 3 行所示)。所以,这就是为什么人们可能会错误地认为像第 1 行这样的东西应该可以工作的原因之一。

也就是说,下面的代码

void barz(int arg[]) {
    arg[2] = 99999;
}

int another_array[] = {0, 1, 2, 3, 4};
barz(another_array);
printf("another_array[2] is %i\n", another_array[2]);

“正确”打印的是99999,而不是2

【讨论】:

  • 这解释了问题,但并没有真正提出解决方案。所以我想我会在这里重复我的评论:如果你只想要一个数组的引用,你想要int (&amp;bar)[10] = foo[0];。这里bar是对int[10]的引用,初始化为数组foo[0]
  • 我认为,您需要阅读一本好的 C++ 书籍,而不是通过尝试和错误来学习语言。可在此处找到精选列表:stackoverflow.com/questions/388242/…
  • @FrançoisAndrieux 甚至auto&amp; ref = foo[0];
  • an int 永远不会被恰当地命名为“别名”,也许你应该看看参考资料
  • 啊啊啊,所以你想要一个别名!是的,它甚至是变量名称的一部分,但直到我读到这个我才真正理解它。然后我第二个@FrançoisAndrieux:你想要的叫做引用,它使用&amp;,你必须使用他的语法。恐怕我提出的重复项表明您为什么会出错,但它没有解释如何做您想做的事。好吧,您可以更新您的问题,明确说明您正在尝试获取别名!
【解决方案2】:

数组定义(即 C 样式数组)中的初始化程序必须是花括号列表。

此规则涵盖第 1 行和第 2 行,您提供的内容不是花括号列表,因此代码不正确。

更多信息:当您确实提供了一个花括号列表时,列表中的每个元素都将作为数组中一个元素的初始化器。作为推论,不可能提供一个会导致多个数组元素被它设置的初始化器。

这条规则几乎是递归应用的;如果你有一个数组数组,那么初始化器应该是一个花括号列表的花括号列表。但是有一条规则,在某些情况下可以省略嵌套大括号(并且代码的行为就像提供了完整的大括号一样)。例如,int x[2][2] = { 1, 2, 3, 4 }; 是允许的,其行为类似于 int x[2][2] = { {1, 2}, {3, 4} };

【讨论】:

    【解决方案3】:

    当您没有指定数组大小时(在第 1 行)。编译器为您指定它。为此,您必须这样做:

    /* Line 0 */    int foo[10][10];
    /* Line 1 */    int foo_alias_compile_error[] = {foo[0]};
    

    但还是有问题。编译该代码时:

     $ g++ error.cpp
     $ error.cpp:4:28: error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
     $    4 |     int fooError[] = {foo[0]};
     $      |                       ~~~~~^
     $      |                            |
     $      |                            int*
    

    因为 foo[0] 是一个指针,并指向矩阵的第一行(在第 1 行)。你可以这样做:

    /* Line 0 */    int foo[10][10];
    /* Line 1 */    int foo_alias_compile_error[] = {foo[0][0]};
    

    【讨论】:

      猜你喜欢
      • 2014-10-15
      • 1970-01-01
      • 1970-01-01
      • 2017-02-27
      • 1970-01-01
      • 2020-02-03
      • 2015-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多