Recentemente, tive um problema ao criar um stringstream
devido ao fato de que assumi incorretamente std::setw()
que afetaria o fluxo de seqüência de caracteres para cada inserção, até que eu o alterasse explicitamente. No entanto, é sempre desabilitado após a inserção.
// 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'
Então, eu tenho várias perguntas:
- Por que é
setw()
assim? - Existem outros manipuladores dessa maneira?
- Existe uma diferença de comportamento entre
std::ios_base::width()
estd::setw()
? - Finalmente, existe uma referência online que documenta claramente esse comportamento? A documentação do meu fornecedor (MS Visual Studio 2005) parece não mostrar isso claramente.
Respostas:
Notas importantes dos comentários abaixo:
Por Martin:
Por Charles:
A seguir, é apresentada a discussão que leva à conclusão acima:
Observando o código, os seguintes manipuladores retornam um objeto em vez de um fluxo:
Essa é uma técnica comum para aplicar uma operação apenas ao próximo objeto aplicado ao fluxo. Infelizmente, isso não os impede de serem pegajosos. Os testes indicam que todos eles, exceto
setw
são pegajosos.Todos os outros manipuladores retornam um objeto de fluxo. Portanto, qualquer informação de estado alterada deve ser registrada no objeto de fluxo e, portanto, permanente (até que outro manipulador altere o estado). Assim, os seguintes manipuladores devem ser manipuladores pegajosos .
Esses manipuladores realmente executam uma operação no próprio fluxo, e não no objeto do fluxo (embora tecnicamente o fluxo faça parte do estado dos objetos do fluxo). Mas não acredito que eles afetem qualquer outra parte do estado dos objetos de fluxo.
A conclusão é que o setw parece ser o único manipulador da minha versão que não é pegajoso.
Para Charles, um truque simples para afetar apenas o próximo item da cadeia:
Aqui está um exemplo de como um objeto pode ser usado para alterar temporariamente o estado e depois devolvê-lo pelo uso de um objeto:
fonte
operator<<
para o manipulador garante que o estado do fluxo seja alterado de uma certa maneira. Nenhum dos formulários configura qualquer tipo de sentinela estadual. É apenas o comportamento da próxima operação de inserção formatada que determina qual parte do estado será redefinida, se houver.setw
parece se comportar de maneira diferente é porque existem requisitos nas operações de saída formatadas para explicitamente.width(0)
o fluxo de saída.O motivo que
width
não parece ser "persistente" é que certas operações têm garantia de chamar.width(0)
um fluxo de saída. Esses são:21.3.7.9 [lib.string.io]:
22.2.2.2.2 [lib.facet.num.put.virtuals]: todas as
do_put
sobrecargas para onum_put
modelo. Estes são utilizados por sobrecargas deoperator<<
tomar umabasic_ostream
e construído em um tipo numérico.22.2.6.2.2 [lib.locale.money.put.virtuals]: todas as
do_put
sobrecargas para omoney_put
modelo.27.6.2.5.4 [lib.ostream.inserters.character]: sobrecargas de
operator<<
pegar umbasic_ostream
e um do tipo de caractere da instanciação basic_ostream ouchar
, assinadochar
ouunsigned char
ou ponteiros para matrizes desses tipos de caracteres.Para ser sincero, não tenho certeza da justificativa para isso, mas nenhum outro estado de um
ostream
deve ser redefinido pelas funções de saída formatadas. Obviamente, coisas comobadbit
efailbit
podem ser definidas se houver uma falha na operação de saída, mas isso deve ser esperado.A única razão pela qual posso pensar para redefinir a largura é que pode ser surpreendente se, ao tentar gerar alguns campos delimitados, seus delimitadores forem preenchidos.
Por exemplo
Para 'corrigir' isso levaria:
enquanto que com uma largura de redefinição, a saída desejada pode ser gerada com o menor:
fonte
setw()
afeta apenas a próxima inserção. É assim quesetw()
se comporta. O comportamento desetw()
é o mesmo queios_base::width()
. Eu obtive minhassetw()
informações em cplusplus.com .Você pode encontrar uma lista completa de manipuladores aqui . A partir desse link, todos os sinalizadores de fluxo devem ser definidos até serem alterados por outro manipulador. Uma nota sobre a
left
,right
einternal
manipuladores: Eles são como as outras bandeiras e que persistem até que sejam alteradas. No entanto, eles só têm efeito quando a largura do fluxo é definida e a largura deve ser definida em todas as linhas. Então, por exemplote daria
mas
te daria
Os manipuladores de entrada e saída não são pegajosos e ocorrem apenas uma vez onde são usados. Os manipuladores parametrizados são diferentes, aqui está uma breve descrição de cada um:
setiosflags
permite que você defina manualmente sinalizadores, cuja lista pode ser encontrada aqui , para que fique adesiva.resetiosflags
comporta-se de maneira semelhante a,setiosflags
exceto que desativa os sinalizadores especificados.setbase
define a base de números inteiros inseridos no fluxo (então 17 na base 16 seria "11" e na base 2 seria "10001").setfill
define o caractere de preenchimento para inserir no fluxo quandosetw
é usado.setprecision
define a precisão decimal a ser usada ao inserir valores de ponto flutuante.setw
torna apenas a próxima inserção a largura especificada preenchendo o caractere especificado emsetfill
fonte
std::hex
também não é pegajoso e, obviamente,std::flush
oustd::setiosflags
também não é pegajoso. Então, eu não acho tão simples assim.std::hex
não ser pegajosa estava errada - eu também descobri isso também. Os sinalizadores de fluxo, no entanto, podem ser alterados, mesmo que você não insira umstd::setiosflags
novo, para que você possa ver isso como não aderente. Além disso, tambémstd::ws
não é pegajoso. Portanto, não é assim tão fácil.