Каков пример различий в разрешенном использовании или поведении между xvalue и prvalue для объектов без POD?

Что такое rvalues, lvalues, xvalues, glvalues ​​и prvalues? дает хороший обзор таксономии rvalues ​​/ lvalues ​​и один из недавних ответов на этот вопрос (https://stackoverflow.com/a/9552880/368896) подчеркивает, что prvalues ​​»похожи» на значения r в старом стиле, тогда как новые xvalues ​​допускают поведение типа lvalue.

Однако рассмотрим следующий код:

class X {};
X foo() { return X(); }

int main()
{
foo() = X(); // foo() is a prvalue that successfully appears on the lhs
}

В этом примере выражение foo() является prvalue, который появляется на левой стороне, и принимает назначение.

Это заставило меня задуматься — логика, что «xvalues» отличаются от «prvalues», потому что xvalues ​​(glvalues, что они есть) могут появляться слева, кажется, нарушается этим примером. Здесь у нас есть prvalue — который не является glvalue — успешно появляется на lhs и принимает назначение.

(Примечание: в случае POD вышеприведенный пример не будет компилироваться, поэтому для POD различие между xvalues ​​и prvalues, похоже, имеет смысл. Поэтому этот вопрос конкретно касается типов, не относящихся к POD.)

Какова же тогда истинная разница в разрешенном использовании или поведении между xvalue и prvalue, что требует, чтобы это различие было записано в стандарте? Единственным примером различий будет хороший альтернативный ответ.

ДОПОЛНЕНИЕ

Комментарий Пабби был верным. Время жизни prvalue увеличивается компилятором, но время жизни xvalue — нет.

Итак, вот ответ на вопрос:

Рассмотрим следующий код:

// ***
// Answer to question, from Pubby's comment
// ***

class X
{
public:
X() : x(5) {}
int x;
};

X foo() { return X(); }
X&& goo() { return std::move(X()); } // terrible coding, but makes the point

int main()
{
foo() = X();
X&& x1 = foo(); // prvalue - lifetime extended!  Object resides directly on stack as return value
X&& x2 = goo(); // xvalue - lifetime not extended.  Object (possibly polymorphic) resides somewhere else.
x1.x = 6;
x2.x = 7; // Danger!

std::cout << x1.x << std::endl; // Just fine
std::cout << x2.x << std::endl; // prints garbage in VS 2012
}

Это демонстрирует разницу в поведении между prvalue и xvalue. Здесь мы имеем идентичный клиентский код, за исключением различий в привязке (prvalue против xvalue).

Как показывает пример кода, время жизни значения prvalue автоматически увеличивается, а время жизни значения xvalue — нет.

Выявлены и другие очевидные различия: для prvalue сам объект отображается в стеке как возвращаемое значение функции; соответственно, поскольку статический тип prvalue гарантированно является его динамическим типом (см. ответ ниже), продление его времени жизни имеет смысл и может быть выполнено компилятором.

С другой стороны, для xvalue объект находится в каком-то неизвестном произвольном месте, поэтому компилятор не может легко продлить его время жизни, особенно учитывая, что тип может быть полиморфным.

Спасибо за ответ.

13

Решение

Для выражений xvalue полиморфного типа не под-типа динамический тип выражения обычно неизвестен во время компиляции (поэтому вычисляется выражение typeid для них, и вызовы виртуальных функций в общем случае не могут быть девиртуализированы).

Для prvalues ​​это не относится. Динамический тип равен статическому типу.

Другое отличие состоит в том, что decltype(e) является ссылочным типом rvalue для значений xvalue и не ссылочным типом для значений prvalue.

Еще одно отличие состоит в том, что преобразование lvalue в rvalue не выполняется для значений prvalue (они уже являются результатом, полученным в результате). Это можно наблюдать по довольно странному коду

struct A {
int makeItANonPod;
A() = default;

private:
int andNonStdLayout;
A(A const&) = default;
};

void f(...);

int main() {
f(A()); // OK
f((A&&)A()); // illformed
}
6

Другие решения

В чем истинная разница между xvalue и prvalue? Xvalue — это разновидность rvalue, которая может быть cv-квалифицирована и ссылаться на объект и имеют динамический тип, равный или неравный статическому типу.

const int&& foo();
int&& _v=foo();

Без xvalue возвращаемое значение вышеупомянутой функции foo может быть только значением rvalue. Но встроенные типы не имеют постоянного значения! Таким образом, приведенная выше неконстантная переменная _v всегда может связать возвращаемое значение foo (), даже если мы хотим, чтобы foo () возвращала константное значение.

0

По вопросам рекламы [email protected]