3

While working on a school assignment, we had to do something with operator overloading and templates. All cool. I wrote:

template<class T>
class Multiplication : public Expression<T>
{
private:
        typename std::shared_ptr<Expression<T> > l, r;

public:
        Multiplication(typename std::shared_ptr<Expression<T> > l, typename std::shared_ptr<Expression<T> > r) : l(l), r(r) {};
        virtual ~Multiplication() {};

        T evaluate() const
        {
                std::cout << "*";
                T ml = l->evaluate();
                T mr = r->evaluate();
                return ml * mr;
        };
};

Then a friend asked me why his code produced output in the "wrong" order. He had something like

T evaluate() const
{
        std::cout << "*";
        return l->evaluate() * r->evaluate();
};

The code of r->evaluate() printed the debug information, before l->evaluate(). I tested it on my machine as well, by just changing these three lines to a one-liner.

So, I thought, well then * should be right-to-left associative. But everywhere on the internet they say it is left-to-right. Are there some extra rules? Maybe something special when using templates? Or is this a bug in VS2012 ?

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287

2 Answers2

8

When we say the associativity of * is left-to-right, we mean that the expression a*b*c*d will always evaluate as (((a*b)*c)*d). That's it. In your example, you only have one operator*, so there isn't anything to associate.

What you're running into is the order of evaluation of operands. You are calling:

operator*(l->evaluate(), r->evaluate());

Both expressions need to be evaluated before the call to operator*, but it is unspecified (explicitly) by the C++ standard what order they get evaluated in. In your case, r->evaluate() got evaluated first - but that has nothing to do with the associativity of operator*.

Note that even if you had a->evaluate() * b->evaluate() * c->evaluate(), that would get parsed as:

operator*(operator*(a->evaluate(), b->evaluate()), c->evaluate())

based on the rules of operator associativity - but even in that case, there's no rule to prevent c->evaluate() from being called first. It may very well be!

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
Barry
  • 286,269
  • 29
  • 621
  • 977
2

You have a single operator in your expression:

l->evaluate() * r->evaluate()

so associativity is not involved at all here. The catch is that the two operands are evaluated before calling the * operator and the order in which they are evaluated is not defined. A compiler is allowed to reorder the evaluation in any suitable way.

In C++11 terms, the call to operator* is sequenced after the operand evaluation, but there is no sequence relation between the two evaluations. From the n4296 draft (post C++14), page 10:

ยง1.9.15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80