user3647351
user3647351

Reputation: 175

Why an insertion in streams makes its width reset to 0?

It seems an insertion causes a stream to reset its width to 0. In other words, you need to repeatedly call the width() whenever you want to align your output, which makes me wonder why. I've looked over the c++11 standard and I found a partial answer, which suggests that width(0) is called inside character inserters according to the ISO C++ standard §27.7.3.6.4.1. However, they are just a subset of the inserters, which is not a complete answer. Then, what about arithmetic inserters? I couldn't find any other references that can explain the behavior of streams.

Upvotes: 3

Views: 122

Answers (1)

James Kanze
James Kanze

Reputation: 153977

Because that's the way the streams were specified. In an << operator, width should be reset, the other formatting options no. I can only guess as to why: presumably, for things like:

std::cout << std::setw(8) << someValue << " cm";

You wouldn't want the width to apply to the string " cm", for example. But you would want to be able to write:

std::cout << std::setw(8) << price << " | " << setw(20) << article;

where article is a string.

Except that, of course, for that to work, you'd have to also change the justification before the string, and the change in justification would be sticky, affecting the next numeric output.

In practice, of course, experienced programmers don't write this sort of code. They'd use something like:

std::cout << price(column1Width) << article.price
    << " | " << label(column2Width) << article.name;

(supposing that they still had to generate tables using a fixed width font). Where price and label were manipulators which set any number of format flags, and restored them in their destructor. (Since they are temporaries, their destructor will be called at the end of the full expression.) This way, this particular line of code doesn't say anything about the physical formatting, but rather that the first value should be formatted as a price, and the second as a label. (And of course, if someone higher up later decides that prices or labels should be formatted differently, you just change the manipulators, rather than searching all of the output statements, and trying to figure out which are prices, and which aren't.)

EDIT (added references to the standard):

It's important to note that the standard cannot cover everything here, since most of the time, you'll be using custom operator<<, written by the author of the class you're outputting. Most of the built-in operator<< are covered by §22.4.2.2.2, in its description of stage 3:

If str.width() is nonzero and the number of charT’s in the sequence after stage 2 is less than str.width(), then enough fill characters are added to the sequence at the position indicated for padding to bring the length of the sequence to str.width().

str.width(0) is called.

For characters and C style strings, this is specified (in much the same way) in §27.7.3.6.4. For std::string, see §21.4.8.8.

For std::complex: the standard defines insertion in terms of other insertion operators. Any setting of width will thus affect only the real element. (Practically speaking, I think we can consider this broken. When I implemented a pre-standard complex class, my << checked the width, and if it was non-zero, subtracted 3 for the non-numeric fields, then divided by 2 and set it before outputting each double. I"m not sure that this is right, but it's certainly better than what the standard specifies. And I also used a semi-colon as separator if the decimal was a comma, as it is in most places I've lived.)

The fact that the other formatting options remain unchanged is because the standard doesn't specify anything, and additional side effects, other than those specified, are assumed to be forbidden.

In all cases, the specification says that stream.width(0) is called. But in the standard, each case is specified separately. There is no specification (even for intent) of any general rule that I can find. Traditionally, however, the rule has always been: reset width, and leave the others unchanged. The principle of least surprise says that you should do this for user defined << (and >>) as well.

Upvotes: 2

Related Questions