¿Qué manipuladores de iomanip son ‘pegajosos’?

Hace poco tuve un problema al crear un stringstream debido al hecho de que asumí incorrectamente que std::setw() afectaría el flujo de cadenas para cada inserción, hasta que lo cambie explícitamente. Sin embargo, siempre se desactiva después de la inserción.

 // 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' 

Entonces, tengo varias preguntas:

  • ¿Por qué es setw() esta manera?
  • ¿Hay otros manipuladores 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.

Notas importantes de los comentarios a continuación:

Por Martin:

@Chareles: Entonces, con 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.

La siguiente es la discusión que conduce a la conclusión anterior:


Al mirar el código, los siguientes manipuladores devuelven un objeto en lugar de una transmisión:

 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 transmisión. 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 Sticky .

 [no]boolalpha [no]showbase [no]showpoint [no]showpos [no]skipws [no]unitbuf [no]uppercase dec/ hex/ oct fixed/ scientific internal/ left/ right 

En realidad, estos manipuladores realizan una operación en la transmisión en sí en lugar de en el objeto de transmisión (aunque técnicamente la secuencia forma parte del estado de objetos de transmisión). 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 solo al siguiente elemento de la cadena:
Aquí hay un ejemplo de cómo se puede usar un objeto para cambiar el estado temporalmente y luego volverlo a colocar mediante el uso de un objeto:

 #include  #include  // 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 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 

El motivo por el que el width no parece ser “adhesivo” es que ciertas operaciones garantizan que se llame a .width(0) en una secuencia de salida. Esos son:

21.3.7.9 [lib.string.io]:

 template basic_ostream& operator< <(basic_ostream& os, const basic_string& str); 

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

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

27.6.2.5.4 [lib.ostream.inserters.character]: Sobrecargas del operator< < toma un basic_ostream y uno del tipo char de la instanciación básica_ostream o char , signed char o unsigned char o punteros a matrices de estos tipos de caracteres.

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 rellenaran sus delimitadores.

P.ej

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

Para 'corregir' 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 reinicio, la salida deseada se puede generar con el más corto:

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

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() . setw() mi información de setw setw() de cplusplus.com .

Puede encontrar una lista completa de manipuladores aquí . Desde ese enlace, todos los indicadores de flujo deben decir establecer hasta que lo cambie otro manipulador. Una nota sobre los manipuladores left , right e internal : Son como las otras banderas y persisten hasta que cambien. 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; 

te daría

 > a > b > c 

pero

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

te daría

 > a >b >c 

Los manipuladores de entrada y salida no son pegajosos y solo ocurren una vez donde se usan. Los manipuladores parametrizados son diferentes, aquí hay una breve descripción de cada uno:

setiosflags permite configurar flags manualmente, una lista de los cuales puede ser generada aquí , por lo que es adhesiva.

resetiosflags comporta de forma similar a setiosflags excepto que desestablece los indicadores especificados.

setbase establece la base de enteros insertados en la secuencia (entonces 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 solo hace que la siguiente inserción tenga el ancho especificado rellenando con el carácter especificado en setfill