Есть ли случай, когда возврат ссылки RValue (&&) полезен?


есть ли причина, когда функция должен вернуть ссылку RValue? Техника, трюк, идиома или паттерн?

MyClass&& func( ... );

Я знаю об опасности возвращения ссылок в целом, но иногда мы делаем это в любом случае, не так ли (T& T::operator=(T) это всего лишь один идиоматических примеру). Но как насчет T&& func(...)? Есть ли какое-то общее место, где мы выиграем от этого? Вероятно, отличается, когда один пишет библиотеку или код API, по сравнению с просто клиентским кодом?

5 69

5 ответов:

есть несколько случаев, когда это уместно, но они относительно редки. Случай возникает в одном примере, когда вы хотите разрешить клиенту перемещаться из элемента данных. Например:

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};

Это следует из комментария Тови. Вы никогда не хотите возвращать ссылки на локальные переменные. Но вы могли бы иметь это:

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.

нет. Просто верните значение. Возвращение литературы в целом-это вовсе не опасно - это возвращение литературы к local переменные, которые опасны. Однако возврат ссылки rvalue довольно бесполезен почти во всех ситуациях (я думаю, если вы писали std::move или что-то).

вы можете вернуться по ссылке, если вы уверены, что ссылочный объект не выйдет из области действия после выхода функции, например, это ссылка на глобальный объект или функция-член, возвращающая ссылку на поля класса и т. д.

Это возвращение справочные правило, точно так же как раз lvalue и rvalue ссылку. Разница заключается в том, как вы хотите использовать возвращенную ссылку. Как я вижу, возвращение по ссылке rvalue редко. Если у вас есть функция:

Type&& func();

вы не вроде такой код:

Type&& ref_a = func();

потому что он эффективно определяет ref_a как тип& поскольку именованная ссылка 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 все еще может быть" украдена " абонентами. На самом деле функции редко определяются для принятия аргументов ссылки rvalue. Наиболее распространенным случаем является то, что" anotherFunc " - это имя класса, а anotherFunc () - это фактически конструктор перемещения.

еще один возможный случай: когда вам нужно распаковать кортеж и передать значения в функцию.

это может быть полезно в этом случае, если вы не уверены в copy-elision.

такой пример:

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 хотя замена.