Классы, Rvalue и Rvalue Ссылки - PullRequest
13 голосов
/ 11 марта 2011

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

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

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

Например:

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&) из списка перегруженных конструкторов, возвращенный временный объект все равно соответствовал бы конструктору rvalue-reference A::A(A&&).

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

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

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

1 Ответ

6 голосов
/ 11 марта 2011

Для меня:

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

Выход:

int conversion ctor
rvalue copy ctor

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

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

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

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

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

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

в обращении кутверждение:

return a;

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

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

...