Не являются ли элементы временного массива rvalues ​​сами?

using intArray = int[];
int (&a) [4] = intArray{1, 2, 3, 4};

Это не допускается, так как нелегально связывать ссылку non-const lvalue с временным (rvalue). Оба g++ 4.9.1 и clang 3.4.2 возвращаются с ошибками; он компилируется отлично, если a const квалифицирован

int const (&a) [4] = intArray{1, 2, 3, 4};

Однако, когда я делаю это

int &x = intArray{1, 2, 3, 4} [1];

оба компилятора скомпилируют его без ошибок. Копая стандарт (черновик N3337) для этого, §5.2.1 Subscripting говорит

1 Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксным выражением. Одно из выражений должно иметь тип "указатель на Т", а другой должен иметь неперечисленное перечисление или интегральный тип. Результатом является lvalue типа "T." Тип "T" должен быть полностью определенным типом объекта. Выражение E1 [E2] идентично (по определению) к * ((E1) + (E2))

2 Список заблокированных-init не должен использоваться со встроенным оператором индексирования.

  • Если я иду с 1, то я не понимаю, почему стандарт позволяет создавать временные массивы, так как подписи в элементе в нем выдают lvalue, т.е. я могу получить lvalue из временного, что противоречит оригиналу понятие временных рядов может быть связано только с константными ссылками или ссылками rvalue.

  • Если я иду с 2, то почему компиляторы не выдают ошибку, когда я делаю {1, 2, 3, 4}[1]?

Ответ 1

Вопрос 1

Правило о невязанных временных значениях для lvalues ​​не обеспечивает безопасность с использованием железа. Он предотвращает часть этого класса ошибок, но не всех. Я подозреваю, что для предотвращения всех таких ошибок понятие "временность" должно быть включено в систему типов, как и const. Тогда вы можете "отбросить временность" в тех случаях, когда вы знаете, что вы не сохраните ссылку дольше, чем срок ее действия. Комитет решил, что правильное правило, которое у нас есть, того стоит, по-видимому, они также решили, что дальнейшие усилия не стоят того.

В другом примере vector<int>(4)[0] также возвращает значение l, даже если вызов operator[] был сделан во временное. Из-за этого стандарт не собирается запрещать создание временных векторов, и я не вижу, чтобы он также запрещал временные массивы. ОК, поэтому vector является определяемым пользователем типом, тогда как массивы встроены, но, кроме того, я думаю, что ситуации похожи.

Если вы используете массивы вообще, и особенно временные, то в какой-то степени стандарт думает, что вы получаете то, что заслуживаете. Он не собирается запрещать временные массивы только потому, что можно получить lvalue из одного.

Я думаю, что у вас есть реальная общая точка. Вероятно, подстрочный индекс можно было бы более безопасно определить в массиве rvalues, поскольку компилятор имеет необходимую информацию. Он может оценивать значение rvalue, значение которого соответствует значению соответствующего элемента массива. Это может быть запутанным или неудобным, поскольку оно не согласуется с обычными выражениями в индексе, но было бы безопаснее:-) Если вы пишете struct A {int a;}, тогда A().a является rvalue, поэтому я не думаю, что это было бы полностью чтобы применить этот принцип к массивам. Разумеется, это будет потрясающее изменение.

Вопрос 2

Вы не используете индекс в списке с привязкой к init-init. Вы используете его на временной основе, которая, как оказалось, была построена с использованием синтаксиса инициализатора нового стиля. То есть ваше выражение анализирует (intArray{1, 2, 3, 4})[1], а не intArray({1, 2, 3, 4}[1]).