Работая над школьным заданием, мы должны были что-то делать с перегрузкой операторов и шаблонами. Все круто Я написал:
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?
Когда мы говорим, ассоциативность *
слева направо, мы имеем в виду, что выражение a*b*c*d
всегда будет оценивать как (((a*b)*c)*d)
, Вот и все. В вашем примере у вас есть только один operator*
так что нечего связывать.
То, с чем вы сталкиваетесь, является порядком оценки операндов. Ты звонишь:
operator*(l->evaluate(), r->evaluate());
Оба выражения должны быть оценены перед вызовом operator*
, но это не определено (явно) стандартом C ++, в каком порядке они оцениваются. В вашем случае, r->evaluate()
оценили в первую очередь — но это не имеет ничего общего с ассоциативностью operator*
,
Обратите внимание, что даже если у вас было a->evaluate() * b->evaluate() * c->evaluate()
, который будет проанализирован как:
operator*(operator*(a->evaluate(), b->evaluate()), c->evaluate())
основанный на правилах ассоциативности операторов — но даже в этом случае нет никаких правил, чтобы предотвратить c->evaluate()
быть вызванным первым. Это может быть очень хорошо!
У вас есть один оператор в вашем выражении:
l->evaluate() * r->evaluate()
поэтому ассоциативность здесь вообще не задействована. Уловка в том, что два операнда вычисляются перед вызовом *
оператор и порядок, в котором они оцениваются, не определены. Компилятору разрешается изменять порядок оценки любым подходящим способом.
В терминах C ++ 11 вызов operator*
секвенируется после оценки операнда, но между этими двумя оценками нет отношения последовательности. От черновик n4296 (пост C ++ 14), страница 10:
§1.9.15 За исключением отмеченных случаев, оценки операндов отдельных операторов и подвыражений отдельных выражений не являются последовательными.