Сохраняет ли rvalue свое «состояние», когда к нему привязывается ссылочный параметр const? - PullRequest
2 голосов
/ 23 марта 2019

Пусть T - произвольный тип. Рассмотрим функцию, которая принимает ссылку const [lvalue]:

void f(const T &obj);

Предположим, что эта функция внутренне выполняет вызов другой функции, которая имеет перегруженную ссылку на rvalue:

void g(T &&obj);

Если мы передадим значение rvalue в f, будет ли вызываться перегрузка ссылки rvalue g или она не сможет это сделать, поскольку она была "преобразована" / привязана к ссылке const lvalue?

Аналогично, если f вызвал функцию, которая принимает значение T по значению,

void h(T obj);

и T имеет конструктор перемещения (т.е. T(T &&);), будет ли вызываться конструктор перемещения или будет вызываться конструктор копирования?

В заключение, если мы хотим обеспечить, чтобы при вызове f для значения rvalue ссылка rvalue передавалась вокруг, сохраняя свое значение rvalue "status", должны ли мы обязательно предоставлять перегрузку ссылки rvalue для f?

Ответы [ 2 ]

3 голосов
/ 23 марта 2019

Категории значений применяются к выражениям , а не к объектам. obj в f является выражением lvalue и поэтому будет рассматриваться как таковое. Обратите внимание, что obj в g является также выражением lvalue; если выражение является именем для объекта, то это lvalue.

Способность, о которой вы говорите, и есть причина, по которой существуют ссылки для пересылки: так что аспекты копирования / перемещения в категории значений выражения аргумента могут быть сохранены посредством вызовов функций. f должен был бы стать шаблоном формы template<typename T> void f(T&& t);, и вам пришлось бы использовать std::forward при передаче его на g.

2 голосов
/ 23 марта 2019

Когда вы используете имя ссылки в качестве выражения, это выражение всегда является lvalue. Неважно, является ли это ссылкой на lvalue или ссылкой на rvalue. Также не имеет значения, была ли ссылка инициализирована lvalue или rvalue.

int &&x = 1;
f(x); // Here `x` is lvalue.

То есть в void f(const T &obj) {...}, obj всегда является lvalue, независимо от того, что вы передаете в качестве аргумента.

Также обратите внимание, что категория значений определяется во время компиляции. Поскольку f не является шаблоном, категория значений каждого выражения в нем не может зависеть от передаваемых вами аргументов.

Таким образом:

Если мы передадим rvalue в f, будет ли ссылочная перегрузка rvalue g называться

номер

если f вызвал функцию, которая принимает экземпляр T по значению, void h(T obj); и T имеет конструктор перемещения (т.е. T(T &&);), будет ли вызываться конструктор перемещения

номер

В заключение, если мы хотим убедиться, что при вызове f для значения rvalue ссылка rvalue передается вокруг, сохраняя свое значение rvalue "status", должны ли мы обязательно предоставлять перегрузку ссылки rvalue для f?

Обеспечение перегрузки является одним из вариантов. Обратите внимание, что в этом случае вы должны явно вызвать std::move в перегрузке rvalue.

Другим вариантом является использование пересылки ссылок , как предлагает Никол Болас:

template <typename T> void f(T &&t)
{
    g(std::forward<T>(t));
}

Здесь std::forward по существу действует как «условный move». Он перемещается t, если ему было передано значение rvalue, и ничего не делает иначе.

...