С++: ассоциативность оператора * (умножения) не слева направо

Работая над школьным заданием, нам нужно было что-то делать с перегрузкой операторов и шаблонами. Все круто. Я написал:

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;
        };
};

Затем друг спросил меня, почему его код выдает результат в «неправильном» порядке. У него было что-то вроде

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

Код r->evaluate() напечатал отладочную информацию до l->evaluate(). Я также проверил это на своей машине, просто изменив эти три строки на одну строку.

Итак, я подумал, что тогда * должен быть ассоциативным справа налево. Но везде в интернете говорят, что слева направо. Есть какие-то дополнительные правила? Может быть, что-то особенное при использовании шаблонов? Или это ошибка в VS2012?


person Martijn Courteaux    schedule 07.06.2015    source источник
comment
Ассоциативность — это не то же самое, что порядок оценки.   -  person Raymond Chen    schedule 07.06.2015
comment
Похоже, это ключ :) Я уже поищу, спасибо.   -  person Martijn Courteaux    schedule 07.06.2015
comment
возможный дубликат Приоритет оператора и порядок оценки   -  person Raymond Chen    schedule 07.06.2015


Ответы (2)


Когда мы говорим, что ассоциативность * — слева направо, мы имеем в виду, что выражение a*b*c*d всегда будет оцениваться как (((a*b)*c)*d). Вот и все. В вашем примере у вас есть только один operator*, поэтому связать нечего.

То, с чем вы сталкиваетесь, - это порядок оценки операндов. Ты звонишь:

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

Оба выражения должны быть оценены перед вызовом operator*, но в стандарте С++ не указано (явно), в каком порядке они оцениваются. В вашем случае r->evaluate() оценивается первым, но это не имеет ничего общего с ассоциативностью operator* .

Обратите внимание, что даже если бы у вас было a->evaluate() * b->evaluate() * c->evaluate(), это было бы проанализировано как:

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

на основе правил ассоциативности операторов, но даже в этом случае нет правила, запрещающего сначала вызывать c->evaluate(). Очень может быть!

person Barry    schedule 07.06.2015

У вас есть один оператор в вашем выражении:

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

так что ассоциативность тут ни при чем. Загвоздка в том, что два операнда оцениваются до вызова оператора *, а порядок, в котором они оцениваются, не определен. Компилятору разрешено переупорядочивать оценку любым подходящим способом.

В терминах C++11 вызов operator* выполняется после вычисления операнда, но между этими двумя вычислениями нет отношения последовательности. Из проекта n4296 (после C++14), стр. 10:

§1.9.15 Если не указано иное, вычисления операндов отдельных операторов и подвыражений отдельных выражений не упорядочены.

person Stefano Sanfilippo    schedule 07.06.2015