25

When does operator << refer to the insertion operator and when does it refer to the bitwise left shift?

This will output 10, and operator << refers to the left shift.

cout << a.b() << a.a.b << endl;  

And this will output 11, operator << refers to the insertion operator.

cout << a.b();
cout << a.a.b ;

I am confused, when will operator << (when use with cout) refer to the left shift operator?

#include <iostream> 
using namespace std; 

class A { 
public:
    A() { a.a = a.b = 1; }

    struct { int a, b; } a;

    int b(); 
}; 

int A::b(){
    int x=a.a;
    a.a=a.b;
    a.b=x; 
    return x;
};

 int main(){
    A a; 
    a.a.a = 0; 
    a.b(); 

    cout << a.b() << a.a.b << endl;      // ?????
    return 0;
}
jwodder
  • 54,758
  • 12
  • 108
  • 124
jmmom
  • 313
  • 3
  • 7
  • 5
    By default it's a "bitwise left shift" operator, which works on `int` like types. This is a built-in facility. If `<<` is overloaded, then it can be used for other purposes. – iammilind Aug 10 '16 at 10:24
  • 7
    IMO this is basic operator precedence and overloading, not worth a question. `when does operator << refer to insertion operator and when it refer to bitwise left shift ? (c++)` When the operand types, subject to precedence/associativity, clearly tell the language which overload to use. – underscore_d Aug 10 '16 at 10:27
  • Possible duplicate of [Operator overloading](http://stackoverflow.com/questions/4421706/operator-overloading) – underscore_d Aug 10 '16 at 10:28
  • 2
    Why did you use that horribly formatted mess of code in an attempt to illustrate your point? That doesn't look like an [MCVE](http://stackoverflow.com/help/mcve) to me, and it invokes UB as **mrtnj** pointed out. Thus it distracts from what is just a basic question about operator overloading, precedence, and associativity. – underscore_d Aug 10 '16 at 10:36
  • 3
    ...and turns it into something totally different about order of evaluation. Had the example been an MCVE and written in a way that people could understand more readily, this wouldn't have been a problem. – underscore_d Aug 10 '16 at 10:43
  • 7
    How did you reach the conclusion that the `10` was the result of bit-shifting? – molbdnilo Aug 10 '16 at 11:36
  • 10
    Who is upvoting this? The wrong question was asked, based on the wrong symptoms and a lack of background reading, with an overly confusing and terribly formatted example. – underscore_d Aug 10 '16 at 14:12
  • 1
    There's only one operator, what it does just depends on how it's overloaded... – user541686 Aug 10 '16 at 17:37
  • @underscore_d this question made the HNQ list, meaning it will get tons of sympathy upvotes. –  Aug 10 '16 at 21:45
  • **correction** to my earlier comment, which I'd delete if not for the fact that it holds the 1st bit of the one after it...: Relying upon the order of evaluation in this case invokes _unspecified_ behaviour, not UB. @Snowman Real sympathy would exist in not giving the question false impressions of its own quality, but what can y'do. – underscore_d Aug 10 '16 at 21:52
  • @underscore_d when you have visitors from sites that have nothing to do with programming and don't know what MCVE or duck debugging mean, it happens. –  Aug 10 '16 at 21:53
  • 2
    It's shocking that such a poor/confusing question can gain so many upvotes in a few hours and is not closed/deleted. – Walter Aug 10 '16 at 22:18
  • After the first two snippets, I had to think for a moment if the output had been turned to binary somehow, since that would have explained the 1 << 1 = 10 "result"... – ilkkachu Aug 11 '16 at 09:25
  • @Walter Right, and now they've accepted the most basic answer available, which only addresses the question's title, not its horribly confused/confusing body - perhaps because they simply couldn't understand all the better answers that explain why the question's premise was fatally flawed to begin with. – underscore_d Aug 11 '16 at 12:05

6 Answers6

41

This will output 10, and operator<< refer to left shift.

cout << a.b() << a.a.b << endl;

This is caused of the fact that order of evaluation of operands is unspecified. With clang it outputs 11 but with gcc it outputs 10.

Your code:

cout << a.b() << a.a.b << endl;

can be replaced with:

std::cout.operator<<(a.b()).operator<<(a.a.b);  

clang first evaluates a.b() then a.a.b, g++ does it the other way around. Since your a.b() modifies variables you get different results.

When you rewrite your code as:

cout << a.b();
cout << a.a.b ;

then you have two full expression statements, there is no unspecified behaviour here related to operand evaluation. So you get always the same result.

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • 3
    Concur. The OP's choice of using strictly `1` and `0` is unfortunate, in that it clouds the eval-order underlying issue. – WhozCraig Aug 10 '16 at 10:40
16

In your case all operator <<s are output stream insertion operators because their left argument is of type ostream&, and they group left to right.

The difference in the output is caused by the order of evaluation of function arguments:

cout << a.b() << a.a.b

is

operator<<(operator<<(cout, a.b()), a.a.b)

so the output depends on which of a.a.b or a.b() is evaluated first. This actually unspecified by current standard (C++14) so you could get 11 as well.

AFAIK in C++17 11 will be the only valid output for both cases because it enforces left-to-right evaluation of function parameters.

Update: this seems to be not true, as the committee decided (as of N4606) to go with indeterminately sequenced parameter evaluation mentioned at the bottom of P0145R2. See [expr.call]/5.

Update2: Since we are talking about overloaded operators here, [over.match.oper]/2 in N4606 applies, which says

However, the operands are sequenced in the order prescribed for the built-in operator.

So indeed, the order of evaluaion will be well-defined in C++17. This misunderstanding apparently has been predicted by the authors of P0145:

We do not believe that such a nondeterminism brings any substantial added optimization benefit, but it does perpetuate the confusion and hazards around order of evaluations in function calls

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Is C++ 17 going to specify the order of function parameter evaluation? – NathanOliver Aug 10 '16 at 12:16
  • 3
    @NathanOliver No, Anton is wrong about that. The change is that `a.a.b` will not be _interleaved_ with `operator<<(cout, a.b())`. So either `operator<<(cout, a.b())` will be fully evaluated before `a.a.b`, or vice versa. So, for example, the order of evaluation `cout` then `a.a.b` then `a.b()` will no longer be conformant. – Oktalist Aug 10 '16 at 13:34
  • 1
    @Oktalist Thanks. I think I found [the paper](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r2.pdf) for that – NathanOliver Aug 10 '16 at 13:38
  • 2
    The change (one of) is that operator<< becomes a sequence point, even when overloaded – Cubbi Aug 10 '16 at 14:23
  • @Oktalist In C++17 the order of evaluation of function parameters will be left to right. Quote from the paper NathanOliver linked: _Every value computation and side effect associated with the initialization, and the initialization itself, of a parameter is sequenced before every value computation and side effect associated with the initialization of any subsequent parameter._ This paper has already been incorporated to the working draft (N4606) – Anton Savin Aug 10 '16 at 21:57
  • @AntonSavin Are you sure? Where is the citation that said paper was voted-in verbatim? **edit** OK, I'll check N4606... Nicol Bolas - a very clued-in C++ user - opened a largely peanut-gallery recent thread on `std-proposals` thusly: "_C++17's CD has apparently voted in the expression evaluation order proposal, but as I understand it, they used the "Alternate Evaluation Order for Function Calls" That says: the expression in the function position is evaluated before all the arguments and the evaluations of the arguments are indeterminately sequenced, but with no interleaving._" So, which is it? – underscore_d Aug 10 '16 at 22:03
  • 1
    @underscore_d yup they've apparently decided to go with indeterminate sequencing, as of N4606, this is quite unfortunate. – Anton Savin Aug 10 '16 at 22:29
  • 1
    While they did unfortunately choose indeterminate sequencing for function arguments (and parameter initialization), they also changed the evaluation rules for overloaded operators, which now evaluate their operands as specified by the corresponding built-in operator, not as a function call. Shift operators are now specified to evaluate left-to-right, so that expression will still have a well-defined evaluation order. (By the way, those are member operator functions.) cc @underscore_d – bogdan Aug 10 '16 at 23:23
  • 1
    @bogdan You are right. The authors of P0145R2 apparently have predicted this - _confusion and hazards_. – Anton Savin Aug 11 '16 at 07:38
  • 1
    Well, they _created_ some confusion of their own, by writing the proposal in such a way as to emphasize the variation that they personally preferred, and failing to present the motivating rationale of the alternative. As @Cubbi pointed out, OP's expression will ultimately be well-defined because of changes to order of evaluation of operands of built-in and overloaded operators, from the same paper. – Oktalist Aug 11 '16 at 13:57
  • 1
    @bogdan Excellent clarification, thank you. I won't take a position on the evaluation order of function arguments (because intuition, but then optimisation, but then _wheee_ in circles we go; it's like I'm really being spammed by that reflector thread again)... but making overloaded operators match their built-in ancestors is IMO a great decision - in the sense of _wtf, you mean it wasn't always like that?_ :-) – underscore_d Aug 11 '16 at 22:41
16

The problem you are confronted with is not concerning the << operator. In each case, the insertion operator is called.

However, you are faced with a problem concerning the order of evaluation in the command line

cout << a.b() << a.a.b << endl;

The function a.b() has a side effect. It swaps the values a.a.a and a.a.b. Thus, it is evident, wether a.b() is called before or after evaluating the value ov a.a.b.

In C++, the order of evaluation is unspecified, see cppreference.com for a more detailed discussion.

ralfg
  • 552
  • 2
  • 11
9

This call:

cout << a.b() << a.a.b << endl;

will first consider:

cout << a.b()

which correspond to the insertion operator and returns a refence to cout. Thus, the instruction will become:

(returned reference to cout) << a.a.b

which again will call the insertion operator and so on...

If your instruction was:

cout << (a.b() << a.a.b) << endl;

the part between parentheses would be considered first:

a.b() << a.a.b

this time, you have an operator between 2 int: compiler can only resolve it as a call to bitwise operator.

rocambille
  • 15,398
  • 12
  • 50
  • 68
9

Binary operators, such as <<, have two properties that define their usage: (operator) precedence and (left- or right-) associativity. In this case, associativity is the key, and, see e.g. http://en.cppreference.com/w/c/language/operator_precedence, the << operator has left-to-right associativity, so they are sequenced (as if by brackets) from left to right:

((cout << a.b()) << a.a.b) << endl;

or in words sequenced as cout << a.b() then << a.a.b and then << endl.

After this sequencing, operator overloading takes effect on each invocation of << with the given types, which then determines which overload is called and thus if it's a cout-operation or a shift.

MicroVirus
  • 5,324
  • 2
  • 28
  • 53
4

Without parenthesis, the operands on both sides of the << determine the meaning: int << int == shift, stream << any == insertion. This 'reuse' of the operator may be confusing, indead. But you can solve ambiguities by using parentheses: stream << (int << int) == "int"

Kento Asashima
  • 312
  • 1
  • 9