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.