Какое хорошее объяснение ошибки в строке 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, но она кажется нерепрезентативной — это массив, поэтому я бы предпочел самодокументировать его как массив.
Ах, это хороший момент. К сожалению, это на графическом процессоре, поэтому ни одна из STL мне недоступна.
Обратите внимание, что в строке 3 содержится указатель на foo[0]
, а это не то, что вам нужно от строки 1. Похоже, вы намереваетесь сделать копию этого массива. Если это так, то линия 3 будет вести себя совсем по-другому.
Вы должны пометить вопрос любым языком шейдеров, который вы используете. Это не вопрос C++, даже если многие языки шейдеров очень похожи на C++.
Возможный дубликат Присвоение одного массива другому массиву С++
спасибо @fabioturati Вот более длинная версия этого stackoverflow.com/questions/4810664/…
@ François Andrieux Я думаю, вы имеете в виду, что я случайно вызываю оператор копирования для массива, что не входило в мои намерения. Я вижу, как может иметь смысл семантика «копировать объект/массив». Придется читать дальше.
Упрощенный пример: int arr[5]; int arr2[5] = arr;
<- ошибка. Вы не можете копировать массивы в C или C++.
@FrançoisAndrieux выглядит как действительный код C++ (несмотря на ошибку компиляции) для меня?
@ragerdl Если вам просто нужна ссылка на массив, вам нужна int (&bar)[10] = foo[0];
. Здесь bar
— ссылка на int[10]
, инициализированная массивом foo[0]
.
@formerlyknownas_463035818 нет, ваш пример другой.
@SergeyA Да, но ОП говорит, что они не могут использовать все функции языка, потому что они будут запускать код на графическом процессоре. Обычно это означает, что это шейдер, код которого очень похож на C и C++. Если это так, тег C++ вводит в заблуждение, и было бы важно пометить фактический используемый язык.
@SergeyA Я запутался, о какой ошибке на самом деле идет речь. мой пример был для строки 2
@FrançoisAndrieux, о, это в комментариях, я пропустил.
@formerlyknownas_463035818 по-прежнему нет, потому что foo[0]
— это массив, а 0
в вашем случае — нет.
@SergeyA Мех, мое замешательство было еще хуже, извините, я выпью пива, ура :P
Спасибо всем! Я включил отзыв в ответ. Я также включил объяснение об источнике моего замешательства в этом ответе. Спасибо еще раз!
когда вы не указываете размер массива (в строке 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]};
Можно было бы думать о строке 1 как о создании ссылки на 0-ю запись foo
. Однако 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 (&bar)[10] = foo[0];
. Здесь bar
— ссылка на int[10]
, инициализированная массивом foo[0]
.
Я думаю, вам нужно прочитать хорошую книгу по C++, а не учить язык методом проб и ошибок. Кураторский список можно найти здесь: stackoverflow.com/questions/388242/….
@FrançoisAndrieux или даже auto& ref = foo[0];
int
никогда не называют «псевдонимом», возможно, вам стоит взглянуть на ссылки
Аааа, так ты хотел псевдоним! Да, это даже часть имен переменных, но я толком этого не понимал, пока не прочитал это. Затем я второй @FrançoisAndrieux: то, что вы хотите, называется ссылкой, в которой используется &
, и вы должны использовать его синтаксис. Боюсь, предложенный мной дубликат указывает, почему вы получаете сообщение об ошибке, но не объясняет, как делать то, что вы хотите. Ну, вы могли бы обновить свой вопрос, дав понять, что вы пытаетесь получить псевдоним!
Инициализатор в определении массива (то есть массив в стиле C) должен должен быть списком в фигурных скобках.
Это правило распространяется как на вашу строку 1, так и на строку 2, где вы предоставляете что-то, что не является списком в фигурных скобках, и поэтому код неверен.
Дополнительная информация: когда вы предоставляете список в фигурных скобках, каждый элемент списка берется в качестве инициализатора для элемента ан массива. Как следствие, невозможно указать один инициализатор, который приводит к установке им нескольких элементов массива.
Это правило почти применимо рекурсивно; если у вас есть массив массивов, то инициализатор должен быть списком фигурных скобок. Однако есть правило, что в некоторых случаях вложенные фигурные скобки могут быть опущены (и код будет вести себя так, как если бы были предоставлены полные скобки). Например, int x[2][2] = { 1, 2, 3, 4 };
разрешено и ведет себя как int x[2][2] = { {1, 2}, {3, 4} };
.
Вместо этого вы можете использовать
std::array<std::array<int, 10>, 10>
. Он самодокументируется, как вы хотите, и ведет себя так, как вы ожидаете, как и другие типы значений.