2

在完成学校作业时,我们不得不对运算符重载和模板做一些事情。都很酷。我写:

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 中的错误?

4

2 回答 2

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()首先被调用。很可能是这样!

于 2015-06-07T15:00:09.343 回答
2

您的表达式中有一个运算符:

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

所以这里根本不涉及关联性。问题是这两个操作数在调用*运算符之前被求值,并且它们的求值顺序没有定义。允许编译器以任何合适的方式重新排序评估。

在 C++11 术语中,调用operator*是在操作数评估之后排序的,但两个评估之间没有顺序关系。来自n4296 草案(C++14 后),第 10 页:

§1.9.15除非另有说明,对单个运算符的操作数和单个表达式的子表达式的求值是无序的。

于 2015-06-07T15:00:40.347 回答