2009-10-07 11 views
117

Recentemente ho avuto un problema nella creazione di uno stringstream a causa del 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.Quali manipolatori iomanip sono "appiccicosi"?

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

Così, ho una serie di domande:

  • Perché setw() in questo modo?
  • Esistono 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.
+0

Un ciclo di lavoro è qui: http://stackoverflow.com/a/37495361/984471 –

risposta

78

Note importanti dai commenti qui sotto:

Di Martin:

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

Di Carlo:

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 flusso di output.

Quello che segue è la discussione che ha portato alla conclusione di cui sopra:


Guardando il codice seguenti manipolatori restituiscono un oggetto piuttosto che un flusso:

setiosflags 
resetiosflags 
setbase 
setfill 
setprecision 
setw 

Questo è un tecnica comune per applicare un'operazione al solo oggetto successivo applicato al flusso. 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 oggetto flusso. Pertanto, qualsiasi informazione di stato che modificano deve essere registrata nell'oggetto del flusso ed è quindi permanente (finché un altro manipolatore non modifica lo stato). Pertanto i seguenti manipolatori devono essere manipolatori Sticky.

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

dec/ hex/ oct 

fixed/ scientific 

internal/ left/ right 

Questi manipolatori effettivamente eseguono un'operazione sul flusso stesso, piuttosto che l'oggetto stream (se tecnicamente il flusso è parte della corrente oggetti di stato). Ma non credo che influiscano su nessuna altra parte dello stato degli oggetti del flusso.

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 effetto solo l'elemento successivo nella catena:
Ecco un esempio di come un oggetto può essere utilizzato per modificare temporaily stato poi rimetterlo con l'uso di un oggetto:

#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

Bel cheat sheet. Aggiungi un riferimento alla provenienza delle informazioni e sarebbe una risposta perfetta. –

+1

Ho esaminato il file "iomanip" nella directory include di gcc. –

+1

Tuttavia, posso verificare che setfill() sia effettivamente "appiccicoso" sebbene restituisca un oggetto. Quindi penso che questa risposta non sia corretta. –

5

setw() riguarda solo l'inserimento successivo. Questo è solo il modo in cui si comporta setw(). Il comportamento di setw() corrisponde a ios_base::width(). Ho ricevuto le mie informazioni setw() da cplusplus.com.

È possibile trovare un elenco completo di manipolatori here. Da quel collegamento, tutti i flag di flusso 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 do persistono fino a quando non vengono modificati. Tuttavia, hanno solo un effetto quando viene impostata la larghezza del flusso e la larghezza deve essere impostata su ogni riga. Così, per esempio

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

darebbe

>  a 
>  b 
>  c 

ma

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

darebbe

>  a 
>b 
>c 

I manipolatori di ingresso e di uscita non sono appiccicosi e si verificano solo una volta dove sono usati. I manipolatori parametrizzati sono diversi, ecco una breve descrizione di ciascuno:

setiosflags consente di impostare manualmente i flag, un elenco dei quali può essere fonte here, quindi è appiccicoso.

resetiosflags si comporta come setiosflags tranne che disattiva i flag specificati.

setbase imposta la base di numeri 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 fa solo il prossimo inserimento della larghezza specificata riempiendo con il carattere specificato in setfill

+3

"Quali manipolatori iomanip sono" appiccicosi "?" – sbi

+0

Bene, molti di loro stanno semplicemente impostando le bandiere, quindi quelle sono "appiccicose". setw() sembra essere l'unico che colpisce solo un inserimento. Puoi trovare ulteriori specifiche per ciascuna su http://www.cplusplus.com/reference/iostream/manipulators/ –

+0

Anche 'std :: hex' non è appiccicoso e, ovviamente,' std :: flush' o ' std :: setiosflags' non sono appiccicosi. Quindi non penso sia così semplice. – sbi

29

Il motivo per cui width non sembra essere "appiccicoso" è che certe operazioni sono garantite per chiamare .width(0) su un flusso di output. Questi sono:

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

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

27.6.2.5.4 [lib.ostream.inserters.character]: Sovraccarico di operator<< prendere una basic_ostream e uno del tipo char del esemplificazione basic_ostream o char, firmato char o unsigned char o puntatori ad array di questo tipo char .

A dire il vero non sono sicuro delle ragioni per questo, ma nessun altro stato di un ostream deve essere ripristinato 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.

E.g.

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

" 4.5  | 3.6  \n" 

Per 'corretta' questo richiederebbe:

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

mentre con una larghezza riarmo, l'uscita desiderata può essere generata con la più corta:

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