0

So the challenge is to write a program with this output:

000042
420000
42
-42-

My first attempt was something like this:

int fortyTwo = 42;

cout << setfill('0') << setw(6) << fortyTwo << endl;
cout << fortyTwo << setfill('0') << setw(6) << endl;
cout << fortyTwo << endl;
cout << setfill('-') << setw(4) << fortyTwo << setfill('-') << endl;

Which gave me something like this:

000042
42
000042
42-- (sometimes just -42)

Here is the author's solution:

cout << setfill('0') << setw(6) << 42 << endl;
cout << left << setw(6) << 42 << endl;
cout << 42 << endl;
cout << setfill('-') << setw(4) << -42 << endl;

Why does the author only use setfill once? How does setfill work for the first two lines but stop all of a sudden at line 3? How does putting setfill('-') and setw(4) before -42 produce -42- instead of --42? What is the left alignment operator needed for?

Finally why doesn't my version produce the correct output?

bmargulies
  • 97,814
  • 39
  • 186
  • 310
DreamBliss
  • 65
  • 1
  • 1
  • 10

3 Answers3

0

setw() only affects the next output. Every other question is pretty much a consequence of this behaviour. That is, unless you try to widen the output, no fill character or alignment will take place.

DanielKO
  • 4,422
  • 19
  • 29
0

std::setw determines the width of the next output field only, but std::setfill sets the fill character used for all further print operations of the stream object it is passed to.

So the fill character does not actually suddenly stop working on the third line of output. But because there is no std::setw manipulator before that output is generated, the fill character is simple not used.

std::left, like std::setfill, takes effect until cancelled. So, after being set for the second line of output, it still takes effect when the fourth line is generated. Since in the fourth line, the fill character is -, this character is appended to the end of the line. (The - at the beginning is due to its being explicitly specified as part of the output string.)


Quotes from the Standard (C++11) for reference.

  1. std::setfill is described in 27.7.4/5:

    unspecified setfill(char_type c);

    Returns: An object of unspecified type such that if out is an object of type basic_ostream and c has type charT then the expression out << setfill(c) behaves as if it called f(out, c), where the function f is defined as:

    template<class charT, class traits>
    void f(basic_ios<charT,traits>& str, charT c) {
        // set fill character
        str.fill(c);
    }
    

    The expression out << setfill(c) shall have type basic_ostream<charT, traits>& and value out.

    So calls the fill() function of the stream, which in turn is described in 27.5.5.3/14:

    char_type fill(char_type fillch);
    Postcondition: traits::eq(fillch, fill())

    In other words, it modifies a property that is part of the traits of the stream. Suffice it to say that traits are valid until explicitly modified (and not only for the next output field).

  2. The situation is similar for std::left, which sets the adjustfield parameter of the stream, as described in 27.5.6.2/3:

    ios_base& left(ios_base& str);
    Effects: Calls str.setf(ios_base::left, ios_base::adjustfield).

  3. std::setw, on the other hand, is described in 27.7.5/7

    unspecified setw(int n);
    Returns: An object of unspecified type such that if out is an instance of basic_ostream<charT, traits> then the expression out << setw(n) behaves as if it called f(out, n), or if in is an object of type basic_istream<charT, traits> then the expression in >> setw(n) behaves as if it called f(in, n), where the function f is defined as:

    void f(ios_base& str, int n) {
      // set width
      str.width(n);
    }
    

    The expression out << setw(n) shall have type basic_ostream& and value out. The expression in >> setw(n) shall have type basic_istream& and value in.

    And indeed, the property width is reset to 0 after the occurrence of a formatted output operation. E.g. in the description of operator<< when applied to strings (i.e. when you use the <<-syntax to output a string), the order of operations in described by 27.7.3.6.4/5 (emphasis mine):

    Padding is determined as described in 22.4.2.2.2. The n characters starting at s are widened using out.widen (27.5.5.3). The widened characters and any required padding are inserted into out. Calls width(0).

    In the case of a formatted output operation for a numeric types (27.7.3.6.2/1,2,3), the std::num_put (which is a std::locale::facet) template is used to convert the number into a string and print it. 22.4.2.2 describes in detail how that operation is performed, and its final step (after the string has been outputted) resets the width to 0 (22.4.2.2.2/5, stage 3, at the end):

    str.width(0) is called

Further study of the effects of the various IO manipulators reveals that in fact, std:setw is the only manipulator that takes effect only on the following (formatted) output operation. See also Which iomanipulators are 'sticky'?, especially Charles Bailey's answer, for a comparison of the "stickiness" of the different manipulators.

Community
  • 1
  • 1
jogojapan
  • 68,383
  • 11
  • 101
  • 131
0

setfill (like most ostream modifiers, e.g. hex, dec, left, the binary one, precision IIRC too) keep their value once you change them.

Let me back up a bit.

std::ostream (the class cout is an instance of) has internal formatting values. Internal variables that tell it what the filler char is, whether to print in hex etc.

Most of these values (maybe all except for width) don't change unless YOU change them. So once you set the fill, it stays that way until YOU change it to something else.

width is different (unfortunately :( I'm sure they had their reasons). After you set it, it only stays for the next item you write - then it resets to 0.

Second thing you seem to have missed, is that the width and fill etc. don't print anything, they only change the internal state and hence apply to what comes after them.

So lets analyze your solution:

You set the fill to 0, set the width to 6, print 42 - which is 2 characters and hence expanded to 6 by adding four 0-s (to the left, because the default is right justified text). Now you're printed something, so width resets (!)

Now you started a new line (endl), printed 42 (bit the width is reset, so it just prints it at whatever width, no need to fill!), set the fill to 0 (it already was! fill stays until you change it), set the width to 6 (but for the next value, not the one already printed!) and go for a new line.

Now you print 42 (width is 6, so it fills the 4 extra chars needed with 0) (the width reset after your print) and go for a new line.

You set the fill to - and the width to 4, and print 42 - which is expanded to 4 characters by adding two - on the left side (not like you wrote... weird that), and set the fill again to - (unneeded, and also unused as you don't print anything after that - setfill and other formattings only affects things that happen after them!)

rabensky
  • 2,864
  • 13
  • 18
  • Thank you for this response. This really helped to explain things to me and now I understand better what's going on. Much appreciated! – DreamBliss Jul 19 '13 at 08:55