2010-07-11 16 views
8

La clase de base de las secuencias de C++ IO std::basic_ios define operator void*() para devolver !fail() y operator!() para devolver fail(). Eso me hace preguntarme por qué necesitamos el operator!() en absoluto. Ciertamente, !is también funcionaría al llamar implícitamente al operator void*() y anular su resultado.¿Por qué std :: basic_ios sobrecarga el operador de negación lógica unario?

¿Me falta algo aquí o es simplemente por razones históricas que se define std::basic_ios::operator!()?

A question on comp.lang.c++.moderated tampoco aportó ninguna respuesta.

+0

Estas son funciones miembro de 'basic_ios', no' ios_base'. –

+0

@Marcelo: Gracias, lo arreglé. – sbi

+0

@Neil: Gracias, me olvidé de eso. De todos modos, parece que tampoco lo sabes? – sbi

Respuesta

5

Con edad (es decir: no mucho después cfront) compiladores de C++, el compilador no estaba garantizada para llamar implícitamente operadores encasillado en los objetos cuando sea necesario. Si iostream no tiene un operator ! declarado, entonces no se puede esperar que !cout funcione en todos los casos. C++ 89 (o lo que sea que se llamó el estándar anterior a C++ 98) simplemente dejó el área indefinida.

Esta es también la razón por la que operator void*() estaba sobrecargado, y no o operator bool. (bool ni siquiera existía como su propio tipo en el estándar en ese punto.) Recuerdo que mi profesor me dijo que if(), bajo el capó, esperaba un void* en C++, porque ese tipo podría actuar como un tipo de "superconjunto" relativo a esos tipos de resultados de expresión que se pasarían a una declaración if, pero no he encontrado esto explicado en ninguna parte.

Esto fue alrededor del tiempo de gcc 2, cuando la mayoría de la gente no soportaba plantillas o excepciones, o si lo hacían, no las soportaba completamente, por lo que la metaprogramación C++ con plantillas era todavía un ejercicio teórico y se aseguraba para comprobar que operator new no devolvió un puntero nulo.

Esto me volvió loco durante varios años.

Un extracto interesante de Stroustrup's The C++ Programming Language, 3rd ed. (1997), página 276:

El istream y ostream tipos se basan en una función de conversión para permitir declaraciones como

while (cin >> x) cout << x; 

La entrada de operación cin >> x devuelve unistream &. Ese valor se convierte implícitamente en un valor que indica el estado de cin. El valor puede entonces probarse por mientras. Sin embargo, normalmente es no una buena idea para definir una conversión implícita de un tipo a otro de tal manera que la información se pierda en la conversión.

Hay mucho en C++ que parece ser una victoria de lo lindo o lo inteligente sobre lo consistente. No me importa si un bit C++ fue lo suficientemente inteligente como para manejar el bucle anterior como:

while (!(cin >> x).fail()) cout << x; 

debido a esto, mientras más detallado y más puntuacion, es más claro que un programador principiante.

... En realidad, ahora que lo pienso, no me gustan ninguno de esos constructos. Deletérelo:

for(;;) 
{ cin >> x; 
    if(!cin) 
     break; 
    cout << x; 
} 

¿Por qué me gusta esto mejor? Porque esta versión hace que sea mucho más claro cómo expandir el código para, por ejemplo, manejar dos lecturas a la vez en lugar de una. Por ejemplo, "El código existente copia una secuencia de valores flotantes. Queremos que lo modifiques de modo que combine los valores flotantes y los escriba, dos por línea, porque ahora estamos usando números complejos".

Pero estoy divagando.

+0

Esa es otra explicación que confirma mi sospecha de que 'operator!()' No se usa para razones técnicas (actuales). En cuanto a la segunda mitad de su respuesta: considero que las corrientes son un diseño muy antiguo. Cosas como 'bueno()' no es lo opuesto a 'malo()', los búferes de flujo realizan _both_ administración de búfer y escriben/leen desde un dispositivo externo, y, bueno, ¡un 'operador! Innecesario()' probablemente no lo haría ser aceptado por, digamos, revisión por pares de boost hoy en día. – sbi

+0

Sí, es un legado, aunque el hecho de que sea explícito y no implícito podría resolver algunas ambigüedades. (No, no puedo pensar en un ejemplo.) Y sí, Streams es un buen ejemplo de alguien que se vuelve listo y hace un monstruo. (Sobrecargando los operadores de turno? Realmente ?! No me inicie en ...) –

+0

Personalmente, estoy molesto con la obsesión de C y C++ por manejar la presentación (formateo) y la entrega (almacenamiento en búfer, archivo de E/S, etc.) en la misma función. ¿Cuántas funciones hay cuyos nombres terminan en 'printf'? Mucho mejor es el enfoque de Python, con el formato y la salida como funciones completamente separadas (el equivalente de C++ tendría una función 'std :: string std :: format (pattern, ...)'). –

1

En cuanto a la implementación de MinGW, que se incluye con Codeblocks me demuestra este código:

operator void*() const 
    { return this->fail() ? 0 : const_cast<basic_ios*>(this); } 

    bool 
    operator!() const 
    { return this->fail(); } 

Me parece, que el operator void*() const está previsto para más usos que sólo para la comprobación de éxito. Además de eso, sirve también como operador de casting (estamos devolviendo this). En este momento me estoy rascando la cabeza un poco, por eso es posible que deseemos emitir this a void*. Pero el resto es bastante claro: si tiene una transmisión errónea de todos modos, también puede devolver el valor nulo.

+3

+1 Pero creo que esto es simplemente un mecanismo para devolver (en el estado OK) un puntero válido y posiblemente ningún valor de captura. –

+1

@Neil: Sí, creo que sí, también. El estándar dice sobre 'operator void *()' (en 27.4.4.3/1): "Devuelve: If' fail() 'luego un puntero nulo, de lo contrario, un puntero no nulo para indicar el éxito." Eso es solo un bool seguro que devuelve '! Fail()'. Acerca de 'operador!() 'dice (en 27.4.4.3/2):" Devuelve: 'fail()'. " – sbi

2

Ok, viniendo seco aquí, fui y asked on comp.lang.c++.moderated.

Al principio, los resultados eran tan malo como han estado aquí, pero al final Daniel Krügler's answer concurrido con mi sospecha de que no hay razones técnicas para operator!():

me han dicho, que esta declaración adicional fue agregada para enfatizar la simetría entre el caso "verdadero" y su negación, solo como una guía para el lector, nada más. Para ser justos, el idioma

operator void* 

era bastante nuevo en este momento y dado este apoyo el que la deducción de sintaxis es proporcionada por esta característica no es inmediatamente evidente. Aparte de eso, no había otra razón técnica para hacerlo. [...]

+3

Daniel Krügler es fácilmente una de las personas más útiles que he encontrado en un foro en línea. Si pudiéramos hacer que se una a la fiesta aquí en Stack Overflow ... –

+1

@James: +1 a eso. Él es como litb ++ (sin ánimo de ofender, Johannes. <3) ​​ – GManNickG

+0

@James, @GMan: Tengo la edad suficiente para recordar realmente que su apellido fue Spangenberg en los 90. ': - /' – sbi

Cuestiones relacionadas