2009-10-07 7 views
117

Recientemente tuve un problema al crear un stringstream debido a que asumí incorrectamente que std :: setw() afectaría el stringstream para cada inserción, hasta que lo cambie explícitamente. Sin embargo, siempre se desactiva después de la inserción.¿Qué manipuladores iomanip son 'pegajosos'?

// With timestruct with value of 'Oct 7 9:04 AM' 
std::stringstream ss; 
ss.fill('0'); ss.setf(ios::right, ios::adjustfield); 
ss << setw(2) << timestruct.tm_mday; 
ss << timestruct.tm_hour; 
ss << timestruct.tm_min; 
std::string filingTime = ss.str(); // BAD: '0794' 

lo tanto, tengo una serie de preguntas:

  • ¿Por qué es setw() de esta manera?
  • ¿Hay algún otro manipulador de esta manera?
  • ¿Hay una diferencia en el comportamiento entre std :: ios_base :: width() y std :: setw()?
  • Finalmente, ¿hay una referencia en línea que documente claramente este comportamiento? La documentación de mi proveedor (MS Visual Studio 2005) no parece mostrar esto claramente.
+0

Una ronda de trabajo es aquí: http://stackoverflow.com/a/37495361/984471 –

Respuesta

78

Notas importantes a partir de los comentarios a continuación:

Por Martin:

@Chareles: Entonces por este requisito todos los manipuladores son pegajosos. Excepto setw que parece ser reiniciado después del uso.

Por Charles:

Exactamente! y la única razón por la cual el setw parece comportarse de manera diferente es porque existen requisitos en las operaciones de salida formateadas para explícitamente .width (0) el flujo de salida.

El siguiente es el debate que condujo a la conclusión anterior:


Mirando el código de los siguientes manipuladores devuelven un objeto en lugar de una corriente:

setiosflags 
resetiosflags 
setbase 
setfill 
setprecision 
setw 

Esta es una técnica común para aplicar una operación solo al siguiente objeto que se aplica a la secuencia. Desafortunadamente, esto no les impide ser pegajosos. Las pruebas indican que todas ellas, excepto setw, son adhesivas.

setiosflags: Sticky 
resetiosflags:Sticky 
setbase:  Sticky 
setfill:  Sticky 
setprecision: Sticky 

Todos los demás manipuladores devuelven un objeto de secuencia. Por lo tanto, cualquier información de estado que cambien debe registrarse en el objeto de transmisión y, por lo tanto, es permanente (hasta que otro manipulador cambie el estado). Por lo tanto, los siguientes manipuladores deben ser Manipuladores adhesivos.

[no]boolalpha 
[no]showbase 
[no]showpoint 
[no]showpos 
[no]skipws 
[no]unitbuf 
[no]uppercase 

dec/ hex/ oct 

fixed/ scientific 

internal/ left/ right 

Estos manipuladores realmente realizar una operación en la corriente de sí mismo en lugar de objeto de flujo (Aunque técnicamente la corriente es parte de la corriente de objetos de estado). Pero no creo que afecten a ninguna otra parte del estado de los objetos de transmisión.

ws/ endl/ ends/ flush 

La conclusión es que setw parece ser el único manipulador en mi versión que no es pegajoso.

Para Charles un simple truco para afectar sólo el siguiente elemento de la cadena:
Aquí hay un ejemplo de cómo un objeto puede ser utilizado para cambiar temporaily el estado luego volverlo a poner por el uso de un objeto:

#include <iostream> 
#include <iomanip> 

// Private object constructed by the format object PutSquareBracket 
struct SquareBracktAroundNextItem 
{ 
    SquareBracktAroundNextItem(std::ostream& str) 
     :m_str(str) 
    {} 
    std::ostream& m_str; 
}; 

// New Format Object 
struct PutSquareBracket 
{}; 

// Format object passed to stream. 
// All it does is return an object that can maintain state away from the 
// stream object (so that it is not STICKY) 
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data) 
{ 
    return SquareBracktAroundNextItem(str); 
} 

// The Non Sticky formatting. 
// Here we temporariy set formating to fixed with a precision of 10. 
// After the next value is printed we return the stream to the original state 
// Then return the stream for normal processing. 
template<typename T> 
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data) 
{ 
    std::ios_base::fmtflags flags    = bracket.m_str.flags(); 
    std::streamsize   currentPrecision = bracket.m_str.precision(); 

    bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']'; 

    bracket.m_str.flags(flags); 

    return bracket.m_str; 
} 


int main() 
{ 

    std::cout << 5.34 << "\n"      // Before 
       << PutSquareBracket() << 5.34 << "\n" // Temp change settings. 
       << 5.34 << "\n";      // After 
} 


> ./a.out 
5.34 
[5.3400000000] 
5.34 
+0

Buena hoja de trucos. Agregue una referencia de donde vino la información, y sería una respuesta perfecta. –

+1

Miré el archivo "iomanip" en el directorio gcc include. –

+1

Sin embargo, puedo verificar que setfill() es de hecho 'pegajoso' aunque devuelve un objeto. Entonces creo que esta respuesta no es correcta. –

5

setw() solo afecta a la siguiente inserción. Así es como se comporta setw(). El comportamiento de setw() es el mismo que ios_base::width(). Obtuve mi información setw() de cplusplus.com.

Puede encontrar una lista completa de manipuladores here. Desde ese enlace, todos los indicadores de flujo deben decir establecer hasta que lo cambie otro manipulador. Una nota acerca de los manipuladores left, right y internal: Son como las otras banderas y do persisten hasta que se modifiquen. Sin embargo, solo tienen un efecto cuando se establece el ancho de la secuencia, y el ancho debe establecerse en cada línea. Así, por ejemplo

cout.width(6); 
cout << right << "a" << endl; 
cout.width(6); 
cout << "b" << endl; 
cout.width(6); 
cout << "c" << endl; 

le daría

>  a 
>  b 
>  c 

pero

cout.width(6); 
cout << right << "a" << endl; 
cout << "b" << endl; 
cout << "c" << endl; 

le daría

>  a 
>b 
>c 

Los manipuladores de entrada y salida no son pegajosas y sólo se producen una vez donde se usan. Los manipuladores parametrizados son cada uno diferente, aquí hay una breve descripción de cada uno:

setiosflags le permite establecer manualmente los indicadores, una lista de los cuales puede ser here, por lo que es adhesivo.

resetiosflags se comporta de forma similar a setiosflags excepto que desestablece las marcas especificadas.

setbase establece la base de enteros insertados en la secuencia (por lo que 17 en la base 16 sería "11", y en la base 2 sería "10001").

setfill establece el carácter de relleno para insertar en la secuencia cuando se usa setw.

setprecision establece la precisión decimal que se utilizará al insertar valores de coma flotante.

setw hace solamente la próxima inserción del ancho especificado rellenando con el carácter especificado en setfill

+3

"¿Qué manipuladores de iomanip son 'pegajosos'?" – sbi

+0

Bueno, la mayoría de ellos solo están poniendo banderas, por lo que son "adhesivas". setw() parece ser el único que afecta solo una inserción. Puede encontrar más detalles para cada uno en http://www.cplusplus.com/reference/iostream/manipulators/ –

+0

. Bien 'std :: hex' también no es pegajoso y, obviamente,' std :: flush' o ' std :: setiosflags' tampoco son pegajosos. Entonces no creo que sea así de simple. – sbi

29

La razón por la que width no parece ser 'adherente' es porque ciertas operaciones garantizan que se llame a .width(0) en una secuencia de salida. Estos son:

21.3.7.9 [lib.string.io]:

template<class charT, class traits, class Allocator> 
    basic_ostream<charT, traits>& 
    operator<<(basic_ostream<charT, traits>& os, 
       const basic_string<charT,traits,Allocator>& str); 

22.2.2.2.2 [lib.facet.num.put.virtuals]: Todos los do_put sobrecargas para la plantilla num_put.Estos son usados ​​por sobrecargas de operator<< teniendo un basic_ostream y un tipo numérico incorporado.

22.2.6.2.2 [lib.locale.money.put.virtuals]: todas las sobrecargas do_put para la plantilla money_put.

27.6.2.5.4 [lib.ostream.inserters.character]: Sobrecargas de operator<< tomar una basic_ostream y uno de los de tipo char la creación de instancias basic_ostream o char, firmaron char o unsigned char o punteros a las matrices de este tipo de char .

Para ser sincero, no estoy seguro de la razón de esto, pero ningún otro estado de un ostream debe restablecerse mediante funciones de salida formateadas. Por supuesto, cosas como badbit y failbit se pueden establecer si hay una falla en la operación de salida, pero eso debería esperarse.

La única razón por la que puedo pensar para restablecer el ancho es que podría sorprender que, al tratar de mostrar algunos campos delimitados, se hayan rellenado los delimitadores.

E.g.

std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n'; 

" 4.5  | 3.6  \n" 

Para 'correcta' esto tomaría:

std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n'; 

mientras que con un ancho de puesta a cero, la salida deseada se puede generar con el más corto:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n'; 
Cuestiones relacionadas