C++:*(乘)运算符的结合性不是从左到右
C++: Associativity of * (multiply) operator is not left-to-right
在完成一项学校作业时,我们不得不处理运算符重载和模板。一切都很酷。我写道:
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;
};
};
然后有朋友问我为什么他的代码会按"wrong"顺序输出。
他有类似
的东西
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 draft (post C++14),第 10 页:
§1.9.15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
在完成一项学校作业时,我们不得不处理运算符重载和模板。一切都很酷。我写道:
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;
};
};
然后有朋友问我为什么他的代码会按"wrong"顺序输出。 他有类似
的东西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 draft (post C++14),第 10 页:
§1.9.15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.