Quali manipolatori iomanip sono “appiccicosi”?

Recentemente ho avuto un problema nella creazione di uno stringstream dovuto al fatto che ho assunto erroneamente che std::setw() avrebbe influenzato lo stringstream per ogni inserimento, finché non l’ho modificato esplicitamente. Tuttavia, è sempre disinserito dopo l’inserimento.

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

Quindi, ho una serie di domande:

  • Perché è setw() questo modo?
  • Sono altri manipolatori in questo modo?
  • C’è una differenza di comportamento tra std::ios_base::width() e std::setw() ?
  • Infine c’è un riferimento online che documenta chiaramente questo comportamento? La documentazione del mio fornitore (MS Visual Studio 2005) non sembra mostrare chiaramente questo.

Note importanti dai commenti seguenti:

Di Martin:

@Chareles: Quindi con questo requisito tutti i manipolatori sono appiccicosi. Tranne setw che sembra essere ripristinato dopo l’uso.

Di Charles:

Esattamente! e l’unica ragione per cui setw sembra comportarsi diversamente è perché ci sono requisiti sulle operazioni di output formattate per esplicitamente .width (0) il stream di output.

Quanto segue è la discussione che porta alla conclusione di cui sopra:


Guardando il codice i seguenti manipolatori restituiscono un object anziché uno stream:

 setiosflags resetiosflags setbase setfill setprecision setw 

Questa è una tecnica comune per applicare un’operazione al solo object successivo applicato al stream. Sfortunatamente questo non impedisce loro di essere appiccicosi. I test indicano che tutti, ad eccezione di setw sono appiccicosi.

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

Tutti gli altri manipolatori restituiscono un object stream. Pertanto, qualsiasi informazione di stato che modificano deve essere registrata nell’object del stream ed è quindi permanente (finché un altro manipolatore non modifica lo stato). Quindi i seguenti manipolatori devono essere manipolatori appiccicosi .

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

Questi manipolatori eseguono effettivamente un’operazione sul stream stesso piuttosto che sull’object stream (sebbene tecnicamente il stream sia parte dello stato degli oggetti stream). Ma non credo che influiscano su nessuna altra parte dello stato degli oggetti del stream.

 ws/ endl/ ends/ flush 

La conclusione è che setw sembra essere l’unico manipolatore della mia versione che non è appiccicoso.

Per Charles un semplice trucco per influenzare solo il prossimo elemento della catena:
Ecco un esempio di come un object può essere usato per modificare temporaneamente lo stato e poi rimetterlo con l’uso di un object:

 #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 

Il motivo per cui la width non sembra essere “appiccicosa” è che certe operazioni sono garantite per chiamare .width(0) su un stream di output. Quelli sono:

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]: tutti do_put overload di do_put per il modello num_put . Questi sono usati dai sovraccarichi operator<< prendendo un basic_ostream e un tipo numerico incorporato.

22.2.6.2.2 [lib.locale.money.put.virtuals]: tutti do_put overload do_put per il modello money_put .

27.6.2.5.4 [lib.ostream.inserters.character]: sovraccarichi operator<< prendendo un basic_ostream e uno del tipo char dell'istanza di base_ostream o char , char firmato o unsigned char o puntatori agli array di questi tipi di char.

Ad essere onesti non sono sicuro della logica per questo, ma nessun altro stato di ostream dovrebbe essere resettato dalle funzioni di output formattate. Naturalmente, cose come badbit e failbit possono essere impostate se c'è un errore nell'operazione di output, ma ciò dovrebbe essere previsto.

L'unica ragione per cui posso pensare di reimpostare la larghezza è che potrebbe essere sorprendente se, quando si tenta di generare dei campi delimitati, i delimitatori siano stati riempiti.

Per esempio

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

Per "correggere" ciò richiederebbe:

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

mentre con una larghezza di ripristino, l'output desiderato può essere generato con il più breve:

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

setw() riguarda solo il prossimo inserimento. Questo è solo il modo in setw() si comporta setw() . Il comportamento di setw() è lo stesso di ios_base::width() . Ho ricevuto le mie informazioni setw() da cplusplus.com .

Puoi trovare un elenco completo di manipolatori qui . Da quel collegamento, tutti i flag di stream dovrebbero dire impostato fino a quando non viene modificato da un altro manipolatore. Una nota sui manipolatori left , right e internal : sono come gli altri flag e persistono fino a quando non vengono modificati. Tuttavia, hanno solo un effetto quando viene impostata la larghezza del stream e la larghezza deve essere impostata su ogni riga. Quindi, per esempio

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

ti darei

 > a > b > c 

ma

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

ti darei

 > a >b >c 

I manipolatori di input e output non sono appiccicosi e si verificano solo una volta dove vengono utilizzati. I manipolatori parametrizzati sono diversi, ecco una breve descrizione di ciascuno:

setiosflags ti consente di impostare manualmente i flag, un elenco dei quali può essere utilizzato qui , quindi è appiccicoso.

resetiosflags comporta in modo simile a setiosflags tranne che distriggers i flag specificati.

setbase imposta la base di interi inseriti nello stream (quindi 17 nella base 16 sarebbe "11" e nella base 2 sarebbe "10001").

setfill imposta il carattere di riempimento da inserire nello stream quando viene utilizzato setw .

setprecision imposta la precisione decimale da utilizzare quando si inseriscono valori in virgola mobile.

setw rende solo il successivo inserimento della larghezza specificata riempiendo il carattere specificato in setfill