29 Beginner The Hundred
The Format That Would Not Leave
one value asked for hex; two of them got it
#include <iomanip>
#include <iostream>
int main() {
int mask = 255;
int slots = 16;
std::cout << "mask: " << std::hex << mask << '\n'; // a bit mask reads better in hex
std::cout << "slots: " << slots << '\n';
std::cout << std::setw(8) << "left" << "right" << '\n';
}
Run it. What does it print?
Answer
slots is still 16 — printed in hex, one statement later. And only left got the width.
Why¶
Almost every manipulator is a setter: std::hex flips the stream's basefield flag and
leaves it flipped, so every integer printed afterwards — later in the statement, later in
the program, inside any function that touches std::cout — comes out in base 16. That is
why slots prints 10. std::setw is the one exception: it sets std::cout.width(), and
every formatted output operation resets the width to 0 as soon as it has consumed it, so
"left" is padded to eight columns and "right" is not. The split is worth memorizing —
setfill, setprecision, boolalpha and showbase all stick; only width is single-use.
Nothing here is malformed code, so -Wall -Wextra says nothing.
The fix¶
Save the flags and put them back, so callers get the stream they lent you:
std::ios::fmtflags saved = std::cout.flags();
std::cout << "mask: " << std::hex << mask << '\n';
std::cout.flags(saved); // restored
std::cout << "slots: " << slots << '\n'; // prints 16
A plain std::cout << std::dec works too, and std::setw simply has to be repeated for
every column you want padded. Since C++20 there is a cleaner escape: std::format takes
its formatting per call and never touches stream state — though libstdc++ only ships
<format> from GCC 13, so g++ 11 rejects the include.
Takeaway: formatting is stream state, not a per-argument option — except setw,
which is spent on the very next item.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo