Thread: << operator and side effects

  1. #1
    Registered User
    Join Date
    Mar 2009
    Posts
    399

    << operator and side effects

    Is the behaviour for this short code snippet unspecified?

    Code:
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
    	int i = 1;
    	
    	cout << ++i << i++ << --i << endl;
    }
    I'm not really sure how the << operator works, but if this gets broken down into a function call I would think that it's unspecified since there's no guarantee in which order that parameters to functions are evaluated.

  2. #2
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    cout << ++i << i++ << --i << endl;
    I'm not entirely sure, but were I to write that, all alarms bells should ring and would consider it unspecified until proved otherwise. Better safe than sorry, no?

    But putting that aside, I believe it is not unspecified. It should return "221" in all cases since the << overload has a lower priority than the postfix and prefix operators and is left-associative.

    EDIT: ... and I'm wrong. Just tested it for fun and is returning 202. I think I will always struggle with operators associativity and priority rules. Hence why I always try to avoid these situations, even if it means more verbose code.
    Last edited by Mario F.; 09-11-2009 at 08:45 AM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  3. #3
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by Memloop View Post
    Is the behaviour for this short code snippet unspecified?

    Code:
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
    	int i = 1;
    	
    	cout << ++i << i++ << --i << endl;
    }
    I'm not really sure how the << operator works, but if this gets broken down into a function call I would think that it's unspecified since there's no guarantee in which order that parameters to functions are evaluated.
    C++ Operator Precedence [C++ Reference]

  4. #4
    Registered User
    Join Date
    Mar 2009
    Posts
    399
    I've already read everything there and it does not explain my question. Here's another example:

    Entropy Overload: C++ evaluation order gotcha

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    It's not order of operation; it's sequence points you need to be concerned about. I am not sitting in the same location as my C++ standard, but if you can find the draft version online that's the word(s) you're looking for.

  6. #6
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    I've already read everything there and it does not explain my question. Here's another example:
    Well, I guess my mistake had shown you it was unspecified behavior

    By outputting 202 it is clearly given a totally unexpected result. But being that not enough (you could always attribute that to a lack of understanding of the rules involved), you do observe correctly that order of evaluation of a function parameters is not specified. So, looking at the << operator overload,

    ostream& operator <<(ostream& os, const ClassType &object)
    becomes evident, I think, it is unspecified.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  7. #7
    Registered User
    Join Date
    Mar 2009
    Posts
    399
    I'm familiar with how sequence points work in C++, but in order to apply them I first need to understand how the whole cout expression is broken down. The link in my post above mentions something about it breaking down into a function call, but I'm not really sure how.

  8. #8
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Overloaded operators are essentially functions with a different call syntax. Replace operator<< with a hypothetical function f and it might look like this:

    Code:
    f(f(f(f(cout, ++i), ++i), --i), endl)
    Now the inner f needs to be called before an outer f (and therefore cout prints things in the correct order), however a right-hand argument may-be evaluated before an inner f is called (since the order of evaluation of arguments is unspecified).

    Hence the output of the program is undefined (and GCC 4.4 issues a warning saying so).

    --------

    So the point is: things are printed in the correct order, but subexpressions are evaluated in an unspecified order. Here's an example, I get the three functions called in reversed order:

    Code:
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    vector<char> calls;
    
    char a() { calls.push_back('a'); return 'a'; }
    char b() { calls.push_back('b'); return 'b'; }
    char c() { calls.push_back('c'); return 'c'; }
    
    int main()
    {
        cout << "Function results: " << a() << b() << c() << '\n';
        cout << "Functions were called in this order: ";
        for (unsigned i = 0; i != calls.size(); ++i) {
            cout << calls[i];
        }
        cout << '\n';
    }
    Last edited by anon; 09-11-2009 at 09:21 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  9. #9
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    It's undefined. You cannot modify the same location more than once in the same expression, unless the two modifications are seperated by a sequence point.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  10. #10
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    And if you are wondering how you go from this
    ostream& operator <<(ostream& os, const ClassType &object)
    to this:
    f(f(f(f(cout, ++i), ++i), --i), endl)
    the << operator returns a reference to its first parameter.

    Code:
    ostream& operator<<(ostream& out, const int& val) {
        out << val;
        return out;
    }
    This allows daisy-chaining of the << operator. And it is why you don't get:

    f(cout, ++i); f(cout, i++); f(cout, --i); f(cout, endl);
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

Popular pages Recent additions subscribe to a feed