2012-01-12 8 views
24

§27.7.3.9 define los siguientes sobrecarga para operator<<:¿Por qué la sobrevalor r de `operator <<` for `basic_ostream` devuelve una referencia lvalue?

template <class charT, class traits, class T> 
    basic_ostream<charT, traits>& 
    operator<<(basic_ostream<charT, traits>&& os, const T& x); 

Efectos:os << x
Devuelve:os

(§27.7.2.6 define la sobrecarga rvalue para operator>>.)
Básicamente, simplemente reenvía a una sobrecarga lvalue. Considero que esta sobrecarga a ser bastante peligroso (el istream uno incluso más que el ostream uno, en realidad), tenga en cuenta lo siguiente:

#include <sstream> 
#include <iostream> 

int main(){ 
    auto& s = (std::stringstream() << "hi there!\n"); 
    std::cout << s.rdbuf(); // oops 
} 

Live example on Ideone (perfecto ejemplo de un comportamiento indefinido imprime nada para mí en MSVC10.).

El ejemplo anterior puede parecer artificial, pero no debe ser demasiado difícil llegar a esta situación en el código genérico o al pasar el (std::stringstream() << "text") a una función que proporciona un valor izquierdo y una sobrecarga rvalue y almacena el std::ostream o en std::istream diferentes formas según la sobrecarga.

Ahora, ¿cuál sería un argumento en contra de devolver un basic_ostream<charT, traits>&& y especificar lo siguiente?

Devuelve: (. Y lo mismo para basic_istream) movimiento (os)

¿Hay algo que estoy pasando por alto? En el estado actual, en mi opinión, parece peligroso y como un defecto. Revisé el LWG issue list y encontré this proposal (hi @HowardHinnant!). De hecho, devuelve un valor r, sin embargo, solo por el beneficio adicional de poder encadenar a este operador especial, no abordando específicamente el problema de seguridad que describí anteriormente (aunque ciertamente lo resuelve). Además, está marcado como cerrado y para reconsideración para el próximo estándar. Como tal, pensé en preguntar aquí:

¿Hay alguna buena razón por la sobrecarga antes mencionada devuelve una referencia lvalue?

+0

por alguna razón, todos los MSVC que he visto parecen permitir que uno enlace valores a una referencia no constante, por lo que devolver el valor de rvalue no cambiará nada para MSVS. –

Respuesta

15

Es un defecto y es mi culpa, lo siento. LWG 1203 (gracias por encontrarme eso! :-)) es mi opinión actual de la solución correcta para el "rvalue-stream-inserter". Tenga en cuenta sin embargo que todavía se podía cogerlo y estar en problemas:

auto&& s = (std::stringstream() << "hi there!\n"); 
std::cout << s.rdbuf(); // oops 

Aunque al menos en el código anterior es un poco más obvia (debido a la &&) que usted está haciendo algo que no debería.

+2

Hm .. luego ...¿No sería incluso mejor no devolver ninguna referencia sino solo un 'basic_ostream ' que se mueve? Eso inhabilitaría el enlace de ref de lvalue y permitiría la semántica adecuada de la extensión de la vida a través de 'auto &&' y 'auto const &', al mismo tiempo que sería capaz de encadenar ese operador como usted propuso. – Xeo

+0

Eso sería más seguro. Pero no estoy seguro de que el golpe de rendimiento valga la pena. Aunque mover un flujo es relativamente barato, no estoy seguro de que sea lo suficientemente barato para hacer en cada operación de encadenamiento. Pero es algo sobre lo que pensar más ... –

+0

Creo que las optimizaciones deberían activarse aquí, evitando casi todos los movimientos (aunque puede ser malo depender de eso). Sin embargo, noté otro pequeño inconveniente: las clases derivadas definidas por el usuario de 'std :: ostream' también necesitarán ser movibles, y puede haber aquellas que contienen un buffer de tamaño fijo que tomaría O (N) tiempo para" mover " . Una vez más, las optimizaciones probablemente harán que esto no sea operativo, pero si no lo hacen, es más severo. – Xeo

Cuestiones relacionadas