Есть ли случай, когда полезно использовать ссылку RValue Reference (&&)?
Есть ли причина, когда функция должна возвращать RValue Reference? Техника или трюк, или идиома или узор?
MyClass&& func( ... );
Мне известно об опасности возврата ссылок в целом, но иногда мы это делаем, не так ли (T& T::operator=(T)
- это всего лишь один идиоматический пример), Но как насчет T&& func(...)
? Есть ли какое-то общее место, где мы выиграем от этого? Возможно, отличается при написании библиотеки или кода API по сравнению с только кодом клиента?
Ответы
Ответ 1
Есть несколько случаев, когда это уместно, но они относительно редки. Случай возникает в одном примере, когда вы хотите разрешить клиенту перемещаться из элемента данных. Например:
template <class Iter>
class move_iterator
{
private:
Iter i_;
public:
...
value_type&& operator*() const {return std::move(*i_);}
...
};
Ответ 2
Это следует за буйным комментарием. Вы никогда не хотите возвращать ссылки на локальные переменные. Но у вас может быть следующее:
vector<N> operator+(const vector<N>& x1, const vector<N>& x2) { vector<N> x3 = x1; x3 += x2; return x3; }
vector<N>&& operator+(const vector<N>& x1, vector<N>&& x2) { x2 += x1; return std::move(x2); }
vector<N>&& operator+(vector<N>&& x1, const vector<N>& x2) { x1 += x2; return std::move(x1); }
vector<N>&& operator+(vector<N>&& x1, vector<N>&& x2) { x1 += x2; return std::move(x1); }
Это должно предотвращать любые копии (и возможные распределения) во всех случаях, за исключением тех случаев, когда оба параметра являются lvalues.
Ответ 3
Нет. Просто верните значение. Возвращение ссылок вообще не является опасным - оно возвращает ссылки на локальные переменные, которые являются опасными. Однако возврат ссылки rvalue практически бесполезен практически во всех ситуациях (думаю, если вы пишете std::move
или что-то еще).
Ответ 4
Вы можете вернуться по ссылке, если вы уверены, что ссылочный объект не исчезнет из области действия после выхода функции, например. это глобальная ссылка на объект или функция-член, возвращающая ссылку на поля класса и т.д.
Это возвращаемое ссылочное правило точно так же, как и для значения lvalue и rvalue. Разница заключается в том, как вы хотите использовать возвращаемую ссылку. Как я вижу, возвращение по ссылке rvalue встречается редко. Если у вас есть функция:
Type&& func();
Вам не понравится такой код:
Type&& ref_a = func();
поскольку он эффективно определяет ref_a как Type & так как имя rvalue reference является значением lvalue, и никакой фактический ход здесь не будет выполнен. Это очень похоже:
const Type& ref_a = func();
за исключением того, что фактическое ref_a является ссылкой на константу без ссылки.
И это также не очень полезно, даже вы прямо передаете func() другой функции, которая принимает Type && аргумент, потому что он по-прежнему является именованной ссылкой внутри этой функции.
void anotherFunc(Type&& t) {
// t is a named reference
}
anotherFunc(func());
Связь func() и anotherFunc() больше похожа на "авторизацию", которой func() соглашается, что anotherFunc() может взять на себя (или вы можете сказать "украсть" ) возвращаемый объект из func(). Но это соглашение очень свободно. Ссылка на не-const lvalue все еще может быть "украдена" вызывающими. На самом деле функции редко определяются для принятия ссылочных аргументов rvalue. Наиболее распространенным случаем является то, что "anotherFunc" - это имя класса, а anotherFunc() на самом деле является конструктором перемещения.
Ответ 5
Еще один возможный случай: когда вам нужно распаковать кортеж и передать значения функции.
Это может быть полезно в этом случае, если вы не уверены в копировании.
Такой пример:
template<typename ... Args>
class store_args{
public:
std::tuple<Args...> args;
template<typename Functor, size_t ... Indices>
decltype(auto) apply_helper(Functor &&f, std::integer_sequence<size_t, Indices...>&&){
return std::move(f(std::forward<Args>(std::get<Indices>(args))...));
}
template<typename Functor>
auto apply(Functor &&f){
return apply_helper(std::move(f), std::make_index_sequence<sizeof...(Args)>{});
}
};
довольно редкий случай, если вы не пишете какую-либо форму замены std::bind
или std::thread
, хотя.