c++ function c++11 lvalue rvalue

c++ - Función que acepta tanto lvalue como rvalue.



function c++11 (4)

¿Hay una manera de escribir una función en C ++ que acepte los argumentos lvalue y rvalue, sin convertirla en una plantilla?

Por ejemplo, supongamos que escribo una función print_stream que lee de un istream e imprime los datos que se leyeron en la pantalla, o algo así.

Creo que es razonable llamar a print_stream esta manera:

fstream file{"filename"}; print_stream(file);

así como así:

print_stream(fstream{"filename"});

Pero, ¿cómo declaro print_stream para que ambos usos funcionen?

Si lo declaro como

void print_stream(istream& is);

entonces el segundo uso no se compilará porque un valor no se vinculará a una referencia de valor no constante.

Si lo declaro como

void print_stream(istream&& is);

entonces el primer uso no se compilará porque un valor de l no se enlazará a una referencia de valor de r.

Si lo declaro como

void print_stream(const istream& is);

entonces la implementación de la función no se compilará porque no se puede leer desde un const istream .

No puedo hacer que la función sea una plantilla y usar una "referencia universal", porque su implementación debe compilarse por separado.

Podría proporcionar dos sobrecargas:

void print_stream(istream& is); void print_stream(istream&& is);

y tengo la segunda llamada a la primera, pero parece una gran cantidad de repeticiones innecesarias, y me parecería muy desafortunado tener que hacer eso cada vez que escribo una función con semántica como esta.

¿Hay algo mejor que pueda hacer?


Otra alternativa bastante fea es hacer de la función una plantilla y crear instancias explícitas de ambas versiones:

template<typename T> void print(T&&) { /* ... */ } template void print<istream&>(istream&); template void print<istream&&>(istream&&);

Esto se puede compilar por separado. El código del cliente solo necesita la declaración de la plantilla.

Sin embargo, yo personalmente me quedaría con lo que Andy Prowl sugiere.


Yo diría que no hay una elección sensata más que ofrecer dos sobrecargas o hacer de su función una plantilla.

Si realmente necesitas una alternativa (fea), entonces supongo que lo único que puedes hacer es que tu función acepte una const& una condición previa que diga que no puedes pasar un objeto de una calificación const . escríbalo (no quiere admitir eso de todos modos). A la función se le permitiría desechar la const de la referencia.

Pero personalmente escribiría dos sobrecargas y definiría una en términos de la otra, así que duplica la declaración, pero no la definición:

void foo(X& x) { // Here goes the stuff... } void foo(X&& x) { foo(x); }


Sé audaz , acepta funciones genéricas y nómbrelas bien.

template<typename Stream> auto stream_meh_to(Stream&& s) -> decltype(std::forward<Stream>(s) << std::string{}){ return std::forward<Stream>(s) << std::string{"meh/n"}; }

Tenga en cuenta que esto funcionará con cualquier cosa que tenga sentido para que funcione, no solo con ostream s. Esa es una buena cosa.

Si se llama a la función con un argumento que no tiene sentido, simplemente ignorará esta definición.

Esta es la misma respuesta de Cube, excepto que estoy diciendo que, cuando es posible, es más elegante no verificar tipos específicos.


// Because of universal reference // template function with && can catch rvalue and lvalue // We can use std::is_same to restrict T must be istream // it''s an alternative choice, and i think is''s better than two overload functions template <typename T> typename std::enable_if< std::is_same<typename std::decay<T>::type, istream>::value >::type print(T&& t) { // you can get the real value type by forward // std::forward<T>(t) }