Является ли `f(). A [0]` значением x?
struct S{
int a[3] = {1,2,3};
};
S&& f(){return S();}
&f().a; //[Error] taking address of xvalue (rvalue reference)
&f().a[0]; //ok in GCC 5.1.0 and Clang 3.6.0
S s;
&static_cast<S&&>(s).a; //[Error] taking address of xvalue (rvalue reference)
&static_cast<S&&>(s).a[0]; //ok in GCC 5.1.0 and Clang 3.6.0
5.7. Выражение представляет собой значение x, если оно:
(7.1) - результат вызова функции, неявно или явно, тип возвращаемого значения которой является ссылкой rvalue на тип объекта,
(7.2) - приведение к ссылке rvalue на тип объекта,
(7.3) - выражение доступа к члену класса, обозначающее нестатический элемент данных не ссылочного типа, в котором выражение объекта является значением x, или
(7.4) - выражение a. * pointer-to-member, в котором первый операнд является значением x, а второй операнд является указателем на элемент данных.
5.2.1 Подписчики Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксным выражением. Одно из выражений должен иметь тип "массив T
" или "указатель на T
", а другой должен иметь незапечатанное перечисление или интегрального типа. Результат имеет тип "T
". Тип "T
" должен быть полностью определенным типом объекта. Выражение E1[E2]
идентично (по определению) на *((E1)+(E2))<<*t
[Примечание: см. 5.3 и 5.7 для деталей *
и +
и 8.3.4 для деталей массивов. -end note], , за исключением того, что в случае операнда массива результат равен lvalue, если этот операнд является lvalue и xvalue в противном случае.
Итак, f().a[0]
значение x?
Я думаю, f().a[0]
должен быть значением x.
[Edit1]
Игнорирование &f().a;
и &f().a[0];
, поскольку 12.2 [class.temporary] p5.2
Время жизни временной привязки к возвращаемому значению в операторе return функции (6.6.3) не является продлен; временное уничтожается в конце полного выражения в заявлении return
static_cast<S&&>(s).a
- значение x (7.2 и 7.3).
", за исключением того, что в случае операнда массива результат равен lvalue, если этот операнд является lvalue и xvalue в противном случае."
Итак, я думаю, что static_cast<S&&>(s).a[0]
должен быть xvalue, но
&static_cast<S&&>(s).a[0]; //ok in GCC 5.1.0 and Clang 3.6.0
Questing:
Неужели я ошибаюсь? Если я ошибаюсь, покажите мне пример, в котором подписи массива будут получены значения xvalue.
Ответы
Ответ 1
Насколько я могу судить, вы действительно правы, это выглядит "ошибкой", хотя, честно говоря, это недавно изменилось с CWG defect 1213 который гласит:
Поскольку операция субтипирования определяется как косвенность через значение указателя, результат оператора индекса, применяемого к массиву xvalue, является значением l, а не значением x. Это может быть удивительно для некоторых.
и это изменило раздел 5.2.1 [expr.sub] следующим образом:
Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксное выражение. Одно из выражений должно иметь тип "массив T" или "указатель на T", а другой должен иметь неперечисленное перечисление или интегральный тип. Результатом является lvalue типа "Т." Тип "T" должен быть полностью определенным типом объекта .62 выражение E1 [E2] идентично (по определению) к * ((E1) + (E2)) [Примечание: см. 5.3 [expr.unary] и 5.7 [expr.add] для деталей * и + и 8.3.4 [dcl.array] для получения подробной информации о массивах. -end note] , за исключением того, что в случае операнда массива результат равен lvalue, если этот операнд это lvalue и xvalue в противном случае.
Таким образом, результат f().a[0];
и static_cast<S&&>(s).a[0]
должен быть x значениями.
У этого дефекта не было предлагаемой резолюции до декабря 2012 года, и поддержка отчета о дефектах clangs перечисляет поддержку этого отчета о дефекте как неизвестного, поэтому, скорее всего, разработчики еще не получили исправления этого дефекта.
Обновить
Подано сообщение отчет об ошибке: Оператор подстроки, применяемый к временному массиву, приводит к значению l.
Ответ 2
Я мог бы протестировать в Clang 3.4.1 std = С++ 11.
Вот мои выводы:
int i = f().a[0]
будет верным: вы получаете ссылку на временную структуру, время жизни временного расширения увеличивается на длительность ссылки, вы берете значение: штраф.
int *i = &f().a[0]
принимается компилятором. Однако предупреждение о f
о том, что вы возвращаете ссылку на локальный временный объект, имеет смысл здесь. Время жизни временного объекта увеличивается на время ссылки: здесь время для копирования адреса. Как только вы берете адрес a
, содержащий объект исчезает, и у вас есть только свисающая ссылка.
int *i = f().a
- это тот же случай, что и предыдущий.
Но когда вы делаете &f().a
, вы берете адрес rvalue типа 'int [3]', и нет смысла брать такой адрес: вы можете принимать его значение.
Отпустите еще один шаг:
S s = f();
является правильным. Вы получаете ссылку на временную структуру, время жизни временного продлевается на время ссылки, вы берете значение: отлично.
Теперь &s.a[0]
является четко определенным указателем на int
, как и int *ix2 = &static_cast<S&&>(s).a[0];
Вы можете даже написать: int (*ix3)[3] = &s.a;
, чтобы принять адрес массива до 3 int
, но по той же причине вы не можете написать &static_cast<S&&>(s).a
, потому что вы берете адрес rvalue типа 'int [ 3] '
TL/DR
С S s = f();
s.a
является четко определенным значением r, s.a[0]
является корректным значением l (вы можете написать s.a[0] = 5;
).
f().s
является rvalue, но с его помощью будет вызываться UB, потому что он заканчивается ссылкой на временный объект, который будет уничтожен, прежде чем вы сможете его использовать.
f().s[0]
может использоваться как хорошо определенное значение r. Вы можете использовать его как lvalue, но по той же причине, что и выше, он будет вызывать UB.