155

I recently had a problem creating a stringstream due to the fact that I incorrectly assumed std::setw() would affect the stringstream for every insertion, until I changed it explicitly. However, it is always unset after the 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'

So, I have a number of questions:

  • Why is setw() this way?
  • Are any other manipulators this way?
  • Is there a difference in behavior between std::ios_base::width() and std::setw()?
  • Finally is there an online reference that clearly documents this behavior? My vendor documentation (MS Visual Studio 2005) doesn't seem to clearly show this.
John K
  • 2,150
  • 2
  • 18
  • 19

3 Answers3

102

Important notes from the comments below:

By Martin:

@Chareles: Then by this requirement all manipulators are sticky. Except setw which seems to be reset after use.

By Charles:

Exactly! and the only reason that setw appears to behave differently is because there are requirements on formatted output operations to explicitly .width(0) the output stream.

The following is the discussion that lead to the above conclusion:


Looking at the code the following manipulators return an object rather than a stream:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

This is a common technique to apply an operation to only the next object that is applied to the stream. Unfortunately this does not preclude them from being sticky. Tests indicate that all of them except setw are sticky.

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

All the other manipulators return a stream object. Thus any state information they change must be recorded in the stream object and is thus permanent (until another manipulator changes the state). Thus the following manipulators must be Sticky manipulators.

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

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

These manipulators actually perform an operation on the stream itself rather than the stream object (Though technically the stream is part of the stream objects state). But I do not believe they affect any other part of the stream objects state.

ws/ endl/ ends/ flush

The conclusion is that setw seems to be the only manipulator on my version that is not sticky.

For Charles a simple trick to affect only the next item in the chain:
Here is an Example how an object can be used to temporaily change the state then put it back by the use of an object:

#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
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Nice cheat sheet. Add a reference to where the information came from, and it would be a perfect answer. – Mark Ransom Oct 07 '09 at 18:07
  • 1
    However I can verify that setfill() is in fact 'sticky' though it returns an object. So I think this answer isn't correct. – John K Oct 07 '09 at 19:03
  • This snippet shows that setprecision is "sticky", it's the fact that `.width(0)` is called either directly or indirectly by all formatted output functions that makes setw appear un-sticky. `int main() { std::ostringstream a; std::cout << a.precision() << ' ' << a.width() << '\n'; a << std::setprecision(7) << std::setw(7); std::cout << a.precision() << ' ' << a.width() << '\n'; a << 5.6; std::cout << a.precision() << ' ' << a.width() << '\n'; return 0; }` – CB Bailey Oct 07 '09 at 19:49
  • 2
    Objects that return a stream __Must__ be sticky, while those that return an object may be sticky but it is not required. I will update the answer with John's Info. – Martin York Oct 07 '09 at 20:42
  • 1
    I'm not sure that I understand your reasoning. All manipulators that take parameters are implemented as free functions returning an unspecified object that acts on a stream when that object is inserted into the stream as this is the only(?) way to preserve the insertion syntax with parameters. Either way, the appropriate `operator<<` for the manipulator ensures that the state of the stream is changed in a certain way. Neither form sets up any sort of state sentry. It's only the behaviour of the next formatted insertion operation that determines what part of the state is reset if any. – CB Bailey Oct 07 '09 at 21:26
  • @Charles: No some manipulaters are implemented as free functions that return a stream. These manipulators must be sticky as there is nowhere to retain any state information. – Martin York Oct 07 '09 at 22:23
  • But the one's that return objects can't usefully hold any state other than the value of there parameters until they are inserted into a stream? They can only have an effect when they are inserted, not when the temporary object is destroyed. E.g. if the object returned by the fictional `nonstick` manipulator tried to do anything when it was destroyed then `cout << nonstick << a << b;` would have completely different behaviour to `cout << nonstick << a; cout << b` and `cout << nonstick; cout << a; cout << b`. This just isn't how (e.g.) std::setw work. – CB Bailey Oct 08 '09 at 06:43
  • @Charles Bailey: See above I have created an example for temporarily changing state of a stream. – Martin York Oct 08 '09 at 08:34
  • You trick is nice... but it's not how standard manipulators are allowed to work. You've added the requirement that the manipulator must be in the same chained expression as the manipulated expression, the standard manipulators are required to work if split across new statements (or even in when split by a comma operator): e.g. `std::cout << PutSquareBracket(); std::cout << 5.34;`. Admittedly, if you were just doing `std::cout << std::setw(9)` on its own you'd probably do `std::cout.width(9)` instead. – CB Bailey Oct 08 '09 at 08:51
  • @Chareles: Then by this requirement all manipulators are sticky. Except setw which seems to be reset after use. – Martin York Oct 08 '09 at 10:22
  • 3
    Exactly! and the only reason that `setw` appears to behave differently is because there are requirements on formatted output operations to explicitly `.width(0)` the output stream. – CB Bailey Oct 08 '09 at 10:48
  • My go-to workaround is, e.g., `std::ostream(s.rdbuf()) << std::oct << 255; s << " is the octal representation of decimal " << 255;`. Otherwise you have to remember what you have to restore and what not, and even if you get it right it becomes a mess. – RFST Oct 06 '18 at 08:34
  • @RFST: That looks like it should work but is a bit non intuitive for a maintainer. Also it becomes a bit burdensome once you have a lot of values to output with diffferent formats. You can always reset the state of the stream to the default value with: `stream.copyfmt(std::ios(nullptr));` See: https://stackoverflow.com/a/4217811/14065 for more details and how to save the state to restore a non default value. – Martin York Oct 06 '18 at 16:03
33

The reason that width does not appear to be 'sticky' is that certain operations are guaranteed to call .width(0) on an output stream. Those are:

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]: All do_put overloads for the num_put template. These are used by overloads of operator<< taking a basic_ostream and a built in numeric type.

22.2.6.2.2 [lib.locale.money.put.virtuals]: All do_put overloads for the money_put template.

27.6.2.5.4 [lib.ostream.inserters.character]: Overloads of operator<< taking a basic_ostream and one of the char type of the basic_ostream instantiation or char, signed char or unsigned char or pointers to arrays of these char types.

To be honest I'm not sure of the rationale for this, but no other states of an ostream should be reset by formatted output functions. Of course, things like badbit and failbit may be set if there is a failure in the output operation, but that should be expected.

The only reason that I can think of for resetting the width is that it might be surprising if, when trying to output some delimited fields, your delimiters were padded.

E.g.

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

"   4.5     |   3.6      \n"

To 'correct' this would take:

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

whereas with a resetting width, the desired output can be generated with the shorter:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • I wonder about the rationale also. Personally I would rather have to do a little more typing than have variations/surprises in behavior. – studgeek Apr 07 '22 at 15:08
7

setw() only affects the next insertion. That's just the way setw() behaves. The behavior of setw() is the same as ios_base::width(). I got my setw() information from cplusplus.com.

You can find a full list of manipulators here. From that link, all the stream flags should stay set until changed by another manipulator. One note about the left, right and internal manipulators: They are like the other flags and do persist until changed. However, they only have an effect when the width of the stream is set, and the width must be set every line. So, for example

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

would give you

>     a
>     b
>     c

but

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

would give you

>     a
>b
>c

The Input and Output manipulators are not sticky and only occur once where they are used. The parameterized manipulators are each different, here's a brief description of each:

setiosflags lets you manually set flags, a list of which can be found here, so it is sticky.

resetiosflags behaves similar to setiosflags except it unsets the specified flags.

setbase sets the base of integers inserted into the stream (so 17 in base 16 would be "11", and in base 2 would be "10001").

setfill sets the fill character to insert in the stream when setw is used.

setprecision sets the decimal precision to be used when inserting floating point values.

setw makes only the next insertion the specified width by filling with the character specified in setfill

Michel de Ruiter
  • 7,131
  • 5
  • 49
  • 74
David Brown
  • 13,336
  • 4
  • 38
  • 55
  • Well, most of them are just setting flags, so those are "sticky." setw() seems to be the only one that affects just one insertion. You can find out more specifics for each at http://www.cplusplus.com/reference/iostream/manipulators/ – David Brown Oct 07 '09 at 16:31
  • Well `std::hex` also isn't sticky and, obviously, `std::flush` or `std::setiosflags` aren't sticky either. So I don't think it's that simple. – sbi Oct 07 '09 at 16:42
  • Just testing hex and setiosflags(), they both seem to be sticky (they both simply set flags that persist for that stream until you change them). – David Brown Oct 07 '09 at 16:51
  • Yeah, the web page that claimed `std::hex` to not to be sticky was wrong - I just found this out, too. Stream flags, however, might changed even if you do not insert a `std::setiosflags` again, so one could see this as non-sticky. Also, `std::ws` isn't sticky either. So it _is_ not _that_ easy. – sbi Oct 07 '09 at 16:54
  • You have put quite some effort into improving your answer. +1 – sbi Oct 09 '09 at 15:10