Является ли `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.