在完成学校作业时,我们必须对操作员重载和模板做一些事情。一切都很酷。我写道:
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中的错误吗?
答案 0 :(得分:8)
当我们说*
的关联性是从左到右时,我们的意思是表达式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()
。它很可能是!
答案 1 :(得分:2)
表达式中只有一个运算符:
l->evaluate() * r->evaluate()
所以这里没有任何关联性。问题是在调用*
运算符之前评估两个操作数,并且未定义它们的计算顺序。允许编译器以任何合适的方式对评估重新排序。
在C ++ 11术语中,在操作数评估之后对operator*
的调用进行了排序,但两次评估之间没有序列关系。来自n4296 draft (post C++14),第10页:
§1.9.15除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不合理的。