Подтвердить что ты не робот

Классы, значения Rvalue и Rvalue

Значение l является значением, связанным с определенной областью памяти, тогда как rvalue является значением выражения, существование которого является временным и которое не обязательно относится к определенной области памяти. Всякий раз, когда lvalue используется в позиции, в которой ожидается значение r, компилятор выполняет преобразование lvalue-to-rvalue и затем продолжает оценку.

http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues

Всякий раз, когда мы создаем временный (анонимный) объект класса или возвращаем временный объект класса из функции, хотя объект является временным, он адресуется. Тем не менее, объект по-прежнему является допустимым значением r. Это означает, что объект: a) адресуемое rvalue или b) неявно преобразовывается из lvalue в rvalue, когда компилятор ожидает использования lvalue.

Например:

class A
{
public:
    int x;
    A(int a) { x = a; std::cout << "int conversion ctor\n"; }
    A(A&) { std::cout << "lvalue copy ctor\n"; }
    A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a) 
{
    return a;
}

int main(void)
{
    &A(5); // A(5) is an addressable object
    A&& rvalue = A(5); // A(5) is also an rvalue
}

Мы также знаем, что временные объекты , возвращаемые (в следующем случае a) функциями: lvalues ​​как этот сегмент кода:

int main(void)
{
    ret_a(A(5));
}

выводит следующий результат:

int conversion ctor

lvalue copy ctor

Указывая, что вызов функции ret_a с использованием фактического аргумента A(5) вызывает конструктор преобразования A::A(int), который строит формальный аргумент функции a со значением 5.

Когда функция завершает выполнение, она затем создает временный объект a, используя a в качестве своего аргумента, который вызывает A::A(A&). Однако, если бы мы удалили A::A(A&) из списка перегруженных конструкторов, возвращаемый временный объект по-прежнему будет соответствовать конструктору reference-reference A::A(A&&).

Это то, что я не совсем понимаю: как объект a может совпадать как с ссылкой rvalue, так и с ссылкой lvalue? Ясно, что A::A(A&) лучше, чем A::A(A&&) (и, следовательно, a должно быть lvalue). Но поскольку ссылка rvalue не может быть инициализирована на lvalue, учитывая, что формальный аргумент a является lvalue, он не должен соответствовать вызову A::A(A&&). Если компилятор делает преобразование lvalue-to-rvalue, это было бы тривиально. Тот факт, что преобразование из "A" в "A &" также тривиально, обе функции должны иметь одинаковые неявные ранжирования последовательностей преобразований, и поэтому компилятор не должен иметь возможности выводить функцию наилучшего соответствия, если оба параметра A::A(A&) и A::A(A&&) находятся в наборе кандидатов перегруженной функции.

Кроме того, вопрос (который я ранее задавал):

Как данный объект может совпадать как с ссылкой на rvalue, так и с ссылкой lvalue?

4b9b3361

Ответ 1

Для меня:

int main(void)
{
    ret_a(A(5));
}

Урожайность:

int conversion ctor
rvalue copy ctor

(т.е. значение r, не lvalue). Это ошибка в вашем компиляторе. Однако это понятно, поскольку правила такого поведения изменились всего несколько месяцев назад (ноябрь 2010 г.). Подробнее об этом ниже.

Когда функция завершает выполнение, он затем создает временный Aобъект, используя A в качестве аргумента, который вызывает A::A(A&).

На самом деле нет. Когда функция ret_a завершает выполнение, она затем создает временный объект A, используя A в качестве своего аргумента, который вызывает A:A(A&&). Это связано с [class.copy]/p33] 1:

Когда критерии элиминации операции копирования выполняются или выполняются за исключением того факта, что источник объект является параметром функции, и подлежащий копированию объект по lvalue, разрешение перегрузки до выберите конструктор для копии. сначала выполнялись так, как если бы объект был обозначается rvalue. Если перегрузка сбой разрешения или если тип первый параметр выбранного конструктор не является ссылкой rvalue к типу объектов (возможно cv-certified), разрешение перегрузки снова, с учетом объект как lvalue. [Примечание: это двухступенчатое разрешение перегрузки должно быть выполняется независимо от того, копия произойдет элиция. Он определяет конструктор, который будет вызываться, если elision не выполняется, а выбранный конструктор должен быть доступен, даже если вызов отменяется. - конечная нота]

Однако, если вы удалите конструктор A::A(A&&), тогда для возврата будет выбран A::A(&). Хотя в этом случае построение аргумента A завершится неудачно, потому что вы не можете его построить, используя rvalue. Однако, игнорируя это на данный момент, я считаю, что ваш последний вопрос:

Как данный объект может соответствовать как rvalue reference и lvalue ссылка?

ссылаясь на инструкцию:

return a;

И ответ находится в приведенном выше параграфе из проекта стандарта: Первое разрешение перегрузки проверяется так, как будто A является rvalue. И если это не удается, разрешение перегрузки снова выполняется с использованием A как lvalue. Этот двухэтапный процесс проверяется только в контексте, в котором разрешено копирование (например, оператор return).

Проект С++ 0x только что был изменен для разрешения двухэтапного процесса разрешения перегрузки при возврате аргументов, которые были переданы по значению (как в вашем примере). И в этом причина различного поведения от разных компиляторов, которые мы видим.