web-dev-qa-db-fra.com

Quels manipulateurs iomanip sont «collants»?

J'ai récemment rencontré un problème lors de la création d'un stringstream car je pensais à tort que std::setw() affecterait le flux de chaînes pour chaque insertion, jusqu'à ce que je le modifie explicitement. Cependant, il est toujours désactivé après l'insertion.

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

J'ai donc un certain nombre de questions:

  • Pourquoi setw() de cette façon?
  • Y a-t-il d'autres manipulateurs de cette façon?
  • Y a-t-il une différence de comportement entre std::ios_base::width() et std::setw()?
  • Enfin, existe-t-il une référence en ligne qui documente clairement ce comportement? Ma documentation fournisseur (MS Visual Studio 2005) ne semble pas le montrer clairement.
130
John K

Notes importantes des commentaires ci-dessous:

Par Martin:

@Chareles: Par cette exigence, tous les manipulateurs sont collants. Sauf setw qui semble être réinitialisé après utilisation.

Par Charles:

Exactement! et la seule raison pour laquelle setw semble se comporter différemment est qu'il existe des exigences sur les opérations de sortie formatées pour explicitement .width (0) le flux de sortie.

Voici la discussion qui a mené à la conclusion ci-dessus:


En regardant le code, les manipulateurs suivants renvoient un objet plutôt qu'un flux:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

Il s'agit d'une technique courante pour appliquer une opération uniquement à l'objet suivant qui est appliqué au flux. Malheureusement, cela ne les empêche pas d'être collants. Les tests indiquent que tous sauf setw sont collants.

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

Tous les autres manipulateurs renvoient un objet de flux. Ainsi, toute information d'état qu'ils changent doit être enregistrée dans l'objet de flux et est donc permanente (jusqu'à ce qu'un autre manipulateur change d'état). Ainsi, les manipulateurs suivants doivent être des manipulateurs Sticky.

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

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

Ces manipulateurs effectuent en fait une opération sur le flux lui-même plutôt que sur l'objet de flux (bien que techniquement le flux fasse partie de l'état des objets de flux). Mais je ne pense pas qu'ils affectent une autre partie de l'état des objets de flux.

ws/ endl/ ends/ flush

La conclusion est que setw semble être le seul manipulateur de ma version qui n'est pas collant.

Pour Charles, une astuce simple pour affecter uniquement l'élément suivant de la chaîne:
Voici un exemple de la façon dont un objet peut être utilisé pour changer temporairement l'état puis le remettre en place en utilisant un objet:

#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
82
Martin York

La raison pour laquelle width ne semble pas être "collant" est que certaines opérations sont garanties d'appeler .width(0) sur un flux de sortie. Ce sont:

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]: Tous do_put surcharges pour le num_put modèle. Ils sont utilisés par des surcharges de operator<< prendre un basic_ostream et un type numérique intégré.

22.2.6.2.2 [lib.locale.money.put.virtuals]: Tous do_put surcharges pour le money_put modèle.

27.6.2.5.4 [lib.ostream.inserters.character]: Surcharges de operator<< prendre un basic_ostream et l'un des types de caractères de l'instanciation basic_ostream ou char, signé char ou unsigned char ou des pointeurs vers des tableaux de ces types de caractères.

Pour être honnête, je ne suis pas sûr de la raison de cela, mais aucun autre état d'un ostream ne devrait être réinitialisé par des fonctions de sortie formatées. Bien sûr, des choses comme badbit et failbit peuvent être définies en cas d'échec de l'opération de sortie, mais cela devrait être prévu.

La seule raison à laquelle je peux penser pour réinitialiser la largeur est qu'il pourrait être surprenant que, lorsque vous essayez de sortir certains champs délimités, vos délimiteurs soient remplis.

Par exemple.

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

"   4.5     |   3.6      \n"

Pour "corriger" cela prendrait:

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

alors qu'avec une largeur de réinitialisation, la sortie souhaitée peut être générée avec la plus courte:

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

setw() n'affecte que la prochaine insertion. C'est juste la façon dont se comporte setw(). Le comportement de setw() est le même que ios_base::width(). J'ai obtenu mes informations de setw() de cplusplus.com .

Vous pouvez trouver une liste complète des manipulateurs ici . À partir de ce lien, tous les drapeaux de flux doivent indiquer set jusqu'à ce qu'ils soient modifiés par un autre manipulateur. Une remarque sur les manipulateurs left, right et internal: Ils sont comme les autres drapeaux et font persistent jusqu'à ce qu'ils soient modifiés. Cependant, ils n'ont d'effet que lorsque la largeur du flux est définie et que la largeur doit être définie à chaque ligne. Ainsi, par exemple

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

te donnerait

>     a
>     b
>     c

mais

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

te donnerait

>     a
>b
>c

Les manipulateurs d'entrée et de sortie ne sont pas collants et ne se produisent qu'une fois où ils sont utilisés. Les manipulateurs paramétrés sont chacun différents, voici une brève description de chacun:

setiosflags vous permet de définir manuellement des indicateurs, dont une liste peut être trouvée ici , donc c'est collant.

resetiosflags se comporte de la même manière que setiosflags sauf qu'il désactive les indicateurs spécifiés.

setbase définit la base des entiers insérés dans le flux (donc 17 en base 16 serait "11", et en base 2 serait "10001").

setfill définit le caractère de remplissage à insérer dans le flux lorsque setw est utilisé.

setprecision définit la précision décimale à utiliser lors de l'insertion de valeurs à virgule flottante.

setw ne fait que la prochaine insertion la largeur spécifiée en remplissant avec le caractère spécifié dans setfill

6
David Brown