¿Hay algún caso en el que sea útil devolver una referencia RValue ( & & )?


¿Hay alguna razón por la que una función debería devolver una referencia RValue? ¿Una técnica, un truco, un modismo o un patrón?

MyClass&& func( ... );

Soy consciente del peligro de devolver referencias en general, pero a veces lo hacemos de todos modos, no lo hacemos (T& T::operator=(T) es solo un ejemplo idiomático). Pero, ¿qué tal T&& func(...)? ¿Hay algún lugar general en el que nos beneficiaríamos de hacer eso? Probablemente diferente cuando se escribe código de biblioteca o API, en comparación con solo el código del cliente?

Author: Community, 2011-04-24

5 answers

Hay algunas ocasiones en las que es apropiado, pero son relativamente raras. El caso aparece en un ejemplo cuando desea permitir que el cliente se mueva de un miembro de datos. Por ejemplo:

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};
 53
Author: Howard Hinnant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-24 13:54:59

Esto sigue el comentario de towi. Nunca desea devolver referencias a variables locales. Pero usted podría tener esto:

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); }

Esto debería evitar cualquier copia (y posibles asignaciones) en todos los casos excepto cuando ambos parámetros son lvalues.

 16
Author: Clinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-11 19:30:05

No. Solo devuelve el valor. Devolver referencias en general no es en absoluto peligroso - es devolver referencias a variables locales lo que es peligroso. Devolver una referencia rvalue, sin embargo, es bastante inútil en casi todas las situaciones (supongo que si estuviera escribiendo std::move o algo así).

 7
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-24 11:34:34

Puede devolver por referencia si está seguro de que el objeto referenciado no saldrá del ámbito después de que la función salga, por ejemplo, es la referencia de un objeto global, o la función miembro que devuelve la referencia a los campos de clase, etc.

Esta regla de referencia de retorno es la misma para las referencias lvalue y rvalue. La diferencia es cómo desea utilizar la referencia devuelta. Como puedo ver, regresar por referencia rvalue es raro. Si tiene función:

Type&& func();

No te gustará código:

Type&& ref_a = func();

Porque define efectivamente ref_a como Type& since named rvalue reference is an lvalue, and no actual move will be performed here. Es bastante como:

const Type& ref_a = func();

Excepto que la ref_a real es una referencia lvalue no constante.

Y tampoco es muy útil incluso pasar directamente func() a otra función que toma un argumento Type&& porque sigue siendo una referencia con nombre dentro de esa función.

void anotherFunc(Type&& t) {
  // t is a named reference
}
anotherFunc(func());

La relación de func () y otrofunc( ) es más como una "autorización" que func() acepta que otro func( ) podría tomar posesión de (o puede decir "robar") el objeto devuelto de func( ). Pero este acuerdo es muy flojo. Una referencia lvalue no constante puede ser "robada" por las personas que llaman. En realidad, las funciones rara vez se definen para tomar argumentos de referencia rvalue. El caso más común es que "anotherFunc" es un nombre de clase y anotherFunc( ) es en realidad un constructor de movimiento.

 0
Author: hangyuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 00:48:49

Un caso más posible: cuando necesita desempaquetar una tupla y pasar los valores a una función.

Podría ser útil en este caso, si no está seguro sobre copy-elision.

Tal ejemplo:

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)>{});
        }
};

Caso bastante raro a menos que estés escribiendo alguna forma de std::bind o std::thread reemplazo.

 0
Author: CoffeeandCode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-04 08:06:50