我们假设我们有一个myClass
个班级存储一个int
。我想重载标准运算符,例如+
,=
,+=
,++
等。如何重载后缀增量,即operator++(int)
,以便在计算表达式后完成实际增量?我的意思是,在以下代码执行bean之后,
myClass object1(0), object2(1);
object1 = object2++ + object2;
我希望object1和object2都保持2。
天真的方法
myClass& operator++(int){
myClass tmp(x) //x - stored value
++x;
return tmp;
}
不起作用,因为在++
之前调用了+
。
我一直在寻找答案,但没有发现任何相关内容。坦率地说,直到最近我才认为有可能这样做。
答案 0 :(得分:3)
您的代码:
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),则行为未定义。 / p>
答案 1 :(得分:1)
正如评论中所提到的 - 这是一个糟糕的主意,因为你的代码的语义与operator++
通常的工作方式的语义不匹配,所以其他人看你的代码,甚至可能在以后的日子里,会感到困惑。
要做到这一点,您需要使用一种名为表达模板的技术。这并不简单,但基本思路是operator++
和operator+
(以及任何其他运算符)将返回新类型的对象(比如说Myclass_expr
)并且该对象会建立起来一个表达,因为它去;因此x++ + x
的结果将是一个小树,表示"此表达式意味着将x添加到x然后增加x"。尚未执行实际算术。
最后,Myclass_expr
将有一个转换运算符Myclass
,其中包含遍历存储表达式并按所需顺序执行所有计算的逻辑。
要查看此特征矩阵库的示例。
答案 2 :(得分:1)
好的,我发现了一个丑陋而简单的方法,所以我会为后代发布它。我在myClass * ptr
添加了myClass
字段。它通常存储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;
时,在计算总和之后进行增量。
答案 3 :(得分:0)
myClass object1(0), object2(1);
object1 = object2++ + object2;
类似于:
int i1 = 0; i2 = 1;
i1 = i2++ + i2;
不保证产生确定性值。 i2++
和i2
的评估不保证从左到右。根据首先评估的内容,该表达式的RHS可以评估为3
或2
。
您应该重新考虑您的代码。
成功:
object1 = object2;
object1 += (object2++);
或
object1 = (object2++);
object1 += object2;
取决于您的意图。