Есть ли случай, когда возврат ссылки RValue (&&) полезен?
Есть ли причина, по которой функция должна возвращать ссылку на RValue? Техника или трюк, или идиома или шаблон?
MyClass&& func( ... );
Я знаю об опасности возвращения ссылок в целом, но иногда мы все равно делаем это, не так ли (T& T::operator=(T)
это только один идиоматический пример). Но как насчет T&& func(...)
? Есть ли какое-нибудь общее место, где мы бы выиграли от этого? Вероятно, отличается, когда кто-то пишет библиотеку или код API, по сравнению с просто клиентским кодом?
5 ответов
Есть несколько случаев, когда это уместно, но они относительно редки. Случай встречается в одном примере, когда вы хотите разрешить клиенту переходить от элемента данных. Например:
template <class Iter>
class move_iterator
{
private:
Iter i_;
public:
...
value_type&& operator*() const {return std::move(*i_);}
...
};
Это следует за комментарием Towi. Вы никогда не хотите возвращать ссылки на локальные переменные. Но вы можете иметь это:
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.
Просто верни значение. Возвращение ссылок вообще не опасно - это возврат ссылок на локальные переменные, что опасно. Однако возврат ссылки на rvalue довольно бесполезен практически во всех ситуациях (думаю, если бы вы писали std::move
или что-то).
Вы можете вернуться по ссылке, если уверены, что указанный объект не выйдет из области видимости после выхода из функции, например, это ссылка на глобальный объект или функция-член, возвращающая ссылку на поля класса и т. Д.
Это возвращаемое правило ссылки одинаково как для lvalue, так и для rvalue. Разница в том, как вы хотите использовать возвращенную ссылку. Как я вижу, возвращение по rvalue-ссылке встречается редко. Если у вас есть функция:
Type&& func();
Вам не понравится такой код:
Type&& ref_a = func();
потому что он эффективно определяет ref_a как Type &, поскольку именованная ссылка на rvalue является lvalue, и здесь никакого фактического перемещения не будет. Это очень похоже на:
const Type& ref_a = func();
за исключением того, что фактический ref_a является неконстантной ссылкой на lvalue.
И это также не очень полезно, даже если вы напрямую передаете func() другой функции, которая принимает аргумент Type&&, потому что она по-прежнему является именованной ссылкой внутри этой функции.
void anotherFunc(Type&& t) {
// t is a named reference
}
anotherFunc(func());
Взаимосвязь func() и anotherFunc() больше похожа на "авторизацию", согласно которой func() соглашается, что anotherFunc() может стать владельцем (или вы можете сказать "украсть") возвращенного объекта из func(). Но это соглашение очень слабое. Неконстантная ссылка lvalue все еще может быть "украдена" вызывающими. На самом деле функции редко определяются, чтобы принимать ссылочные аргументы. Наиболее распространенным случаем является то, что "anotherFunc" - это имя класса, а anotherFunc() - фактически конструктор перемещения.
Еще один возможный случай: когда вам нужно распаковать кортеж и передать значения в функцию.
Это может быть полезно в этом случае, если вы не уверены в праве копирования.
Такой пример:
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
замена хотя.