Подтвердить что ты не робот

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

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

MyClass&& func( ... );

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

4b9b3361

Ответ 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, хотя.