c++ - ¿Hay algún caso en que una devolución de una referencia de RValue(&&) sea útil?
c++11 move-semantics (5)
Esto da seguimiento al comentario de towi. Nunca desea devolver referencias a variables locales. Pero puedes 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 sean valores l.
¿Hay alguna razón por la cual una función debería devolver una Referencia de RValue ? Una técnica, o truco, o una expresión idiomática o un patrón?
MyClass&& func( ... );
Soy consciente del peligro de devolver referencias en general, pero a veces lo hacemos de todos modos, ¿no? ( T& T::operator=(T)
es solo un ejemplo idiomático). Pero ¿qué hay de T&& func(...)
? ¿Hay algún lugar general donde ganaríamos al hacer eso? ¿Probablemente diferente cuando uno escribe la biblioteca o el código API, en comparación con el código del cliente?
Hay algunas ocasiones cuando 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_);}
...
};
No. Solo devuelve el valor. Las referencias que vuelven en general no son para nada peligrosas; es peligroso devolver referencias a variables locales . Sin embargo, devolver una referencia de valor razonable es bastante inútil en casi todas las situaciones (supongo que si estuviera escribiendo std::move
o algo así).
Puede devolver por referencia si está seguro de que el objeto al que se hace referencia no saldrá del alcance después de que la función finalice, por ejemplo, es una referencia de objeto global o una función de miembro que devuelve referencia a los campos de clase, etc.
Esta regla de referencia de devolución es la misma para ambas referencias, lvalue y rvalue. La diferencia es cómo desea utilizar la referencia devuelta. Como puedo ver, volver por referencia de valor es raro. Si tienes función:
Type&& func();
No le gustará ese código:
Type&& ref_a = func();
porque define efectivamente ref_a como Tipo y dado que la referencia del valor r es un valor l, y no se realizará ningún movimiento real aquí. Es como:
const Type& ref_a = func();
excepto que el ref_a real es una referencia de valor l no const.
Y tampoco es muy útil, incluso si pasas directamente func () a otra función que toma un argumento Tipo && porque todavía es una referencia nombrada dentro de esa función.
void anotherFunc(Type&& t) {
// t is a named reference
}
anotherFunc(func());
La relación de func () y anotherFunc () es más como una "autorización" que func () acepta que otroFunc () pueda apropiarse (o puede decir "robar") el objeto devuelto de func (). Pero este acuerdo es muy flexible. Las personas que llaman aún pueden "robar" una referencia de valor l no const. 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.
Un caso más posible: cuando necesita descomprimir una tupla y pasar los valores a una función.
Podría ser útil en este caso, si no está seguro sobre la elisión de copia.
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 muy raro, a menos que esté escribiendo alguna forma de reemplazo std::bind
o std::thread
.