Какие выражения создают значения x?

Я пытаюсь понять концепции C ++ 11.

Стандартный черновик, который у меня есть, говорит:

Значение xvalue (значение «eXpiring») также относится к объекту, обычно ближе к концу его жизненного цикла (например, чтобы его ресурсы можно было перемещать). Xvalue - это результат определенных видов выражений, содержащих ссылки на rvalue (8.3.2). [Пример: результат вызова функции, возвращаемый тип которой - ссылка rvalue, - это xvalue. —Конечный пример]

Хорошо, так что именно представляют собой «определенные виды выражений», которые производят значения x? В этой части спецификации не приводится подробный список этих выражений.

Я понимаю lvalue и prvalue (по крайней мере, я думаю, я понимаю).


person PermanentGuest    schedule 20.07.2012    source источник
comment
Полный ответ: что такое rvalues, lvalues, xvalues, glvalues ​​и prvalues ?   -  person pmr    schedule 20.07.2012
comment
Эта часть стандарта объясняет общее понимание этого термина. Он не детализирует все виды выражений, генерирующих значение x. Это происходит в другом месте стандарта.   -  person Nicol Bolas    schedule 20.07.2012
comment
@NicolBolas: Я читал этот вопрос и ответы раньше, и я решил опубликовать еще один вопрос именно по этой причине.   -  person PermanentGuest    schedule 20.07.2012
comment
@PermanentGuest: Итак, ваш вопрос в том, какие выражения генерируют значения x. Вам следует отредактировать свой вопрос, чтобы прояснить, что вы ищете, откуда берутся значения x, а также отредактировать его, чтобы люди знали, что вы это читали, но это конкретно не делает это ясно.   -  person Nicol Bolas    schedule 20.07.2012
comment
@NicolBolas: Спасибо за редактирование .. Извините, я не смог ответить вам на выходных ..   -  person PermanentGuest    schedule 23.07.2012


Ответы (4)


Во введении к §5 (C ++ 11 §5 [expr] / 6) есть полезное ненормативное примечание:

[Примечание: выражение является значением x, если оно:

  • результат неявного или явного вызова функции, возвращаемый тип которой является ссылкой rvalue на тип объекта,

  • приведение к ссылке rvalue на тип объекта,

  • выражение доступа к члену класса, обозначающее нестатический член данных не ссылочного типа, в котором выражение объекта является значением x, или

  • .* выражение указателя на член, в котором первый операнд является значением x, а второй операнд - указателем на член данных.

В общем, действие этого правила состоит в том, что именованные ссылки rvalue обрабатываются как lvalue, а безымянные ссылки rvalue на объекты обрабатываются как xvalue; Ссылки rvalue на функции обрабатываются как lvalue независимо от того, имеют ли они имена. —В конце примечания]

Если просмотреть оставшуюся часть §5, этот список кажется исчерпывающим. За списком следует пример:

struct A {
    int m;
};

A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A&&>(a);

Выражения f(), f().m, static_cast<A&&>(a) и a + a являются значениями x. Выражение ar - это lvalue.

Есть два распространенных способа получить выражение xvalue:

  • Используйте std::move для перемещения объекта. std::move выполняет static_cast тип ссылки rvalue и возвращает ссылку rvalue.

  • Используйте std::forward для пересылки rvalue. std::forward обычно используется в шаблоне функции, чтобы обеспечить безупречную пересылку аргумента функции.

    Если аргумент, предоставленный шаблону функции, был rvalue, типом параметра будет ссылка rvalue, которая является lvalue. В этом случае std::forward выполняет static_cast ссылочный тип rvalue и возвращает ссылку rvalue.

    (Примечание: если аргумент, предоставленный шаблону функции, был lvalue, типом параметра будет ссылка lvalue, а std::forward вернет ссылку lvalue.)

person James McNellis    schedule 26.07.2012
comment
Бу, как раз в тот момент, когда я составлял свой список примеров и наткнулся на цитату. :( +1, надо было отредактировать тег после того, как я закончил свой ответ. :) - person Xeo; 26.07.2012
comment
Спасибо за подробный список ... это в сочетании с другими ответами и множеством других поисковых запросов несколько устранило путаницу по поводу основного определения. - person PermanentGuest; 27.07.2012
comment
Очень полезный ответ, но один момент все еще ускользает от моего понимания. Я понимаю, что в Foo bar(){Foo foo; return foo;} выражение foo является значением x в операторе возврата (исправьте, если ошиблись). Я не вижу этого в приведенном вами списке. - person ricab; 20.07.2015
comment
@ricab Я думаю, что foo было бы prvalue. xvalue всегда имеет тип ссылки rvalue и может быть назначен ссылке rvalue. Это то, что я понимаю до сих пор. - person rents; 10.10.2016
comment
Я имел в виду, что foo будет lvalue. Подумав об этом немного больше, xvalue - это rvalue, которому МОЖЕТ БЫТЬ присвоена некоторая идентичность. И только значения rvalue, которые могут иметь идентификатор, имеют тип && в основном, когда мы приводим их явно: std :: move или возвращаемся из функции. Таким образом, выражение foo - это lvalue, а функция вызова bar () - это xvalue. Вы можете назначить bar () объекту Foo && и присвоить ему идентификатор. - person rents; 10.10.2016

В разделе 5, в котором описывается синтаксис допустимых выражений, перечислены для каждого синтаксиса выражения условия, в которых выражение является lvalue, xvalue или prvalue. Полный список возможных значений x из раздела 5:

5.2.2 параграф 10: вызов функции - это ... значение x, если тип результата является ссылкой rvalue на тип объекта.

(На техническом языке Стандарта «тип объекта» не означает то же самое, что «тип класса». «Тип объекта» включает основные типы, указатели и массивы и исключает только типы функций. Ссылка rvalue на тип функции всегда рассматривается как lvalue, а не как xvalue.)

Наиболее заметными функциями, которые возвращают ссылку на rvalue, являются, конечно, std::move, а иногда и std::forward.

5.2.5 абзац 4: Если E2 является нестатическим элементом данных ... если E1 является значением x, тогда E1.E2 является значением x

(С другой стороны, поиск элемента данных E1->E2 всегда является lvalue.)

Точно так же, если E1 является значением x, тогда поиск элемента данных E1.*E2 является значением x:

5.5 абзац 6. Результат выражения .*, второй операнд которого является указателем на элемент данных, имеет ту же категорию значений (3.10), что и его первый операнд.

Для различных типов слепков:

  • dynamic_cast<Type>(expr): 5.2.7 параграф 2
  • static_cast<Type>(expr): 5.2.9 абзац 1
  • reinterpret_cast<Type>(expr): 5.2.10 параграф 1
  • const_cast<Type>(expr): 5.2.11 абзац 1
  • (Type) expr: 5.4 абзац 1

выражение является значением x тогда и только тогда, когда Type является ссылкой rvalue на тип объекта. То же верно и для Type(expr), поскольку

5.2.3 абзац 1: Если список выражений [в скобках после имени типа] является единственным выражением, выражение преобразования типа эквивалентно (по определенности и, если определено по смыслу) соответствующему выражению приведения (5.4).

(С другой стороны, Type{expr} всегда является prvalue.)

Раздел 5.16 об условном операторе заканчивается тем, что A ? B : C иногда может быть значением x, если B и / или C являются значениями x. Но полные правила сложно резюмировать.

Если выражение заканчивается вызовом определяемой пользователем перегруженной операторной функции, то раздел 5.2.2 применяется к этому выражению, а не к тому, которое описывает поведение встроенного оператора. (См. Выражение a + a в опубликованном примере @James.)

person aschepler    schedule 26.07.2012

Как вы упомянули,

Значение xvalue (значение «eXpiring») также относится к объекту, обычно ближе к концу его жизненного цикла (например, чтобы его ресурсы можно было перемещать).

Фокус: object, be moved, end lifetime

Вот пример:

void move_test(){
    std::string s = "I'm here!";
    std::string m  =  std::move(s);  // move from <s> to <m>
    // s is now in an undefined, but valid state; 
    std::cout << "s=" << s << "; &s=" << &s << std::endl;
}

Перемещая, мы можем преобразовать именованное значение (lvalue, здесь s) в rvalue (то есть std::move(s)). В частности, поскольку он не может быть prvalue (у него есть имя! Другими словами, у него есть личность), он в конечном итоге становится xvalue.

xvalue всегда обслуживает семантику перемещения C ++.

person Eureka Bing    schedule 02.03.2021

Из того, что я прочитал, я понял, что называть что-то xvalue - это причудливый способ сказать:

Xvalue - это просто rvalue, хранилище которого могло быть освобождено, поэтому его использование означает, что вы должны сами проверить его существование.

Как правило, это один или несколько уровней косвенного обращения от фактического rvalue.

Напротив, rvalue гарантированно будет иметь пространство для хранения, пока оно находится в области видимости.

Возможно, я ошибаюсь, но это то, что я понял.

person user541686    schedule 26.07.2012
comment
Нет, xvalue - это значение, которое все еще действует, но будет отброшено. Как правило, это означает, что можно безопасно делать такие вещи, как кража ресурсов и перезапись существующего значения. - person aschepler; 26.07.2012
comment
@aschepler: Так, например, тип возвращаемого значения []() { int x = 5; return std::move(x); } не является значением x? Если нет, то что это? - person user541686; 26.07.2012
comment
Типы не имеют категорий значений; выражения делать. - person aschepler; 26.07.2012
comment
@aschepler: Ладно, а как насчет ([]() { int x = 5; return std::move(x); })()? - person user541686; 26.07.2012
comment
Лямбда-выражение без конечного возвращаемого типа и содержащее более одного оператора имеет неявный конечный возвращаемый тип void. Если вы переместите объявление за пределы лямбда-выражения, неявный конечный возвращаемый тип будет int, потому что преобразование lvalue-to-rvalue применяется, когда необходимо вывести возвращаемый тип. Если вы сделаете это []()-> int&& {...}, конечно, это выражение будет xvalue, потому что это вызов функции воображаемой структуры operator(). Но тогда нет никакого способа определить, что это висячая ссылка. - person aschepler; 26.07.2012
comment
@aschepler: Да, я забыл тип возвращаемого значения. Я действительно имел в виду int&&. Так вы говорите мне, что это значение x? Конечно, но теперь вы просто говорите в точности то же самое, что я сказал в своем ответе (значение rvalue, память которого, возможно, была освобождена) ... почему вы сказали, что это неправильно? - person user541686; 26.07.2012
comment
позвольте нам продолжить это обсуждение в чате - person aschepler; 26.07.2012