超载后增量

Overloading postincrement

本文关键字:超载      更新时间:2023-10-16

假设我们有一个类myClass存储一个int。我想重载标准运算符,如+=+=++等。如何重载后缀增量,即operator++(int),以便在计算表达式后进行实际增量?我的意思是,在执行了以下代码之后,

myClass object1(0), object2(1);
object1 = object2++ + object2;

我希望对象1和对象2都保持2。

的幼稚方法

myClass& operator++(int){
  myClass tmp(x) //x - stored value
  ++x;
  return tmp;
}

不起作用,因为++是在+之前调用的。

我一直在寻找答案,但没有找到任何相关的答案。坦率地说,直到最近我才认为这是可能的。

您的代码:

myClass object1(0), object2(1);
object1 = object2++ + object2;

根据§8.3.6/9:,具有实际未指明的行为

函数参数的求值顺序未指定。

由于这个表达式被转换为类似的东西

operator=(objec1, operator+(operator++(.., object2), object))

避免这种不便的明显方法是在总和之后执行增量:

object1 = object + object2;
object++;

或者(假设operator*也被定义(:

object1 = object2 * 2;
object++;

根据§1.9/15:,如果它们是标量类型,则将是未定义的行为

除非另有说明,否则单独运算符的操作数和单独表达式的子表达式的求值都是无序列的。[注:在程序执行过程中多次求值的表达式中,其子表达式的未排序和不确定排序的求值不需要在不同的求值中一致执行。--结束语]运算符的操作数的值计算是在运算符结果的值计算之前排序的。如果一个标量对象上的副作用相对于同一标量对象上另一个副作用或使用相同标量对象的值进行的值计算是未排序的,并且它们不可能并发(1.10(,则行为是未定义的。

正如评论中所提到的,这是一个糟糕的想法,因为代码的语义与operator++正常工作的语义不匹配,所以其他查看代码的人,甚至可能是以后的自己,都会感到困惑。

要做到这一点,您需要使用一种名为表达式模板的技术。这并不简单,但基本思想是operator++operator+(以及任何其他运算符(将返回一个新类型的对象(比如Myclass_expr(,并且该对象在执行过程中构建一个表达式;所以CCD_ 15的结果将是一个小树,上面写着"这个表达式意味着把x加到x上,然后增加x"。还没有进行实际运算。

最后,Myclass_expr将具有一个到Myclass的转换运算符,该运算符包含遍历存储表达式并按所需顺序执行所有计算的逻辑。

要查看此示例,请查看特征矩阵库。

好吧,我找到了一种丑陋但简单的方法,所以我将它发布给子孙后代。我在myClass中添加了一个myClass * ptr字段。它通常存储nullptr,但operator++(int)返回的对象除外。在这种情况下,ptr存储一个指向应该递增的对象的指针。我还修改了析构函数,以便在临时对象被析构函数时执行增量:

    myClass& operator++(int){
      myClass tmp(x);
      tmp.ptr = this;
      return tmp;
    }
    ~myClass(){
      if(ptr != nullptr)
        ++ptr->x;
    }

虽然它不是标准化的,但tmp通常在达到分号时被破坏,即当执行x++ + x + x;时,在计算和之后进行增量。

myClass object1(0), object2(1);
object1 = object2++ + object2;

类似于:

int i1 = 0; i2 = 1;
i1 = i2++ + i2;

这不能保证产生确定性值。CCD_ 25和CCD_ 26的评估不能保证从左到右。根据最先求值的对象,该表达式的RHS可以求值为32

您应该重新思考您的代码。

成功:

object1 = object2;
object1 += (object2++);

object1 = (object2++);
object1 += object2;

这取决于你的意图。