Hi all,

I've recently had some issues with the streaming operator and the
order of precedence used when mixing it with the member function
operator (operator .) Example code is below.

#include <iostream>
#include <vector>
using namespace std;

class A
{
   public:
       A() : i(0) {};
       int f() {
           i++;
           v.push_back(i);
           return i;
       }

       void reset() { i = 0; v.clear(); }

       int i;
       vector<int> v;
};

int main()
{
   A a;

   // Order of action:
   // - Member functions fire from RIGHT to LEFT.
   // - Insertion operators fire from LEFT to RIGHT
   cout << a.f() << a.f();
   cout << endl;

   cout << "Vector contents a: ";
   for (unsigned int i = 0; i < a.v.size(); ++i )
   {
       cout << a.v.at(i);
   }
   cout << endl;

   a.reset();
   int k;
   // Order of action;
   // - Member functions fire from LEFT to RIGHT
   // - Addition operator fires.
   // - Assignment operator fires.
   k = a.f() + 3*a.f();
   cout << "k: " << k << endl;
   cout << "Vector contents a: ";
   for (unsigned int i = 0; i < a.v.size(); ++i )
   {
       cout << a.v.at(i);
   }
   return 0;
}

*********************************************************************************
OUTPUT:
21
Vector contents a: 12
k: 7
Vector contents a: 12
*********************************************************************************

The result of this seems to indicate that the . operator for the
member function call has different associativities in the different
situations. I must be missing something here, but I haven't been able
to find anyone to give me a conclusive answer on it. Can anyone here
shed some light on it?

Cheers,
Daniel.

I don't have a conclusive answer for you, but I don't think the effect you see is related to the dot operator, per se. What you're seeing is the same order of operations as with simple types as well.

The insertion operator ( << ) works right to left - the rightmost operand is evaluated and inserted into the stream represented by the left operand. In the addition, evaluation is left to right, so your methods get evaluated in that order.

I could be wrong, but I thought associativity was more to do with what the compiler does when it sees two operators of the same type in front of it?

e.g.

// According to the standard, << is left-right, so the following are the same
cout << 5 << endl; 
((cout << 5 ) << endl; )

// According to the standard, = right-left, so the following are the same
a = b = 2;
(a = (b = 2));

And for both of these it makes sense. In the first case there is no streaming function which would know what to do with 5 as its LHS argument and endl as its rhs argument. For the second case, there is no way the compiler could do a=b if b was previously undefined itself.

Around a single binary operator like << and =, it doesn't really matter the order. LHS and RHS simply constitute the first and second arguments to the operator. The return type can be anything you wish. This is obvious when you overload one of these operators, you don't have to return any particular type. However...the associativity of the operator is something that I dont think you can change - its built in to the operator. Or at least I thought till I ran into the above problem.

the order that operands are executed is not really defined, so you should really not have operations that have side effects and operations that are affected by those side effects in the same expression

you have the code

(cout << a.f()) << a.f();

in this particular case, they evaluated the right side of the << first, so the second a.f() returns 1, and then later they evaluated the thing in parentheses

but another compiler could do it the other way around, and evaluate the thing in the parenthesis first, and then your output would be "12"

you should separate side effects into different statements if you don't want to run into this kind of stuff

the order that operands are executed is not really defined, so ..

According to the list here I figured that given the member function accessors were in rank 2 and anytime there was more than one of them in a single operation it would always parse them from left to right (not ambiguously as above).

Is that list not fully defined in the c++ standard or is it a problem with compilers?

>Can anyone here shed some light on it?
Gladly. The problem is that you're expecting a well defined order of evaluation where none exists. The language standard says that between sequence points, the compiler can evaluate the expression in any way it likes as long as the result is correct and consistent. This is most noticeable with functions, because the order in which the functions are called in the statement is unspecified.

>member function accessors were in rank 2 and anytime there
>was more than one of them in a single operation it would always
>parse them from left to right (not ambiguously as above)
Associativity has nothing to do with it. The actual insertions are being made in left to right order, but the functions aren't required to be called in that order unless you separate them with a sequence point:

cout << a.f();
cout << a.f();
This article has been dead for over six months. Start a new discussion instead.