C++ 为什么前inc和后inc/减量操作符是分开实现的?
我试图找到增量运算符和减量运算符的re版本和post版本分别可重载的基本原理。C++ 为什么前inc和后inc/减量操作符是分开实现的?,c++,operator-overloading,C++,Operator Overloading,我试图找到增量运算符和减量运算符的re版本和post版本分别可重载的基本原理。 在我看来,在我所见过的任何类型的类的每个实现中,这些运算符都是相同的运算符(=做相同的事情),只是在调用时有所不同。 我认为,C++的设计者会有一个代码> +++/COD>运算符,编译器会根据需要在读取值之前或之后调用它(或者,更可能的是,在前一个或下一个序列点,我认为是等价的)。 因此,问题是:是否有人有一个案例/类的例子,其中这些案例/类的实现可能不同?或者有人知道/猜测这种设计选择背后的基本原理吗 对于那些喜
在我看来,在我所见过的任何类型的类的每个实现中,这些运算符都是相同的运算符(=做相同的事情),只是在调用时有所不同。
我认为,C++的设计者会有一个代码> +++/COD>运算符,编译器会根据需要在读取值之前或之后调用它(或者,更可能的是,在前一个或下一个序列点,我认为是等价的)。 因此,问题是:是否有人有一个案例/类的例子,其中这些案例/类的实现可能不同?或者有人知道/猜测这种设计选择背后的基本原理吗
对于那些喜欢看代码而不是阅读问题文本的人,以下是总结: 对于哪种类型的
T
(一个用户定义的类,表示您想要的任何内容),以下两行没有相同的副作用是否有意义:
T v;
v++;
++v;
编辑
下面引用@Simple的评论,希望能澄清问题: 如果编译器 可以自己复制,然后进行预增量
编辑2
由于许多人显然不清楚这个问题,下面是另一种解释: 考虑以下两行:
b = a++;
b = ++a;
如果它是一个运算符(为了论证,我将调用运算符+a+),编译器将第一行翻译为
b = a;
+a+;
第二个进入
+a+;
b = a;
因为这些操作符对于内置类型具有单独的语义。表达式的值在递增/递减前后不同,即使两者都更改了操作数
int a = 1;
(a++) == 1;
a = 1;
(++a) == 2;
允许单独重载它们允许为返回值创建类似的语义 Pre increment在语句的其余部分之前递增变量,例如
x = 2;
y = ++x;
y == 3;
x == 3;
而post increment则在语句的其余部分之后执行增量
x = 2;
y = x++;
y == 2;
x == 3;
预增量稍微快一点,因此应优先使用。需要注意的是,当在一个语句中使用两个运算符时,行为是未定义的,因此
x = 5;
x = x++ + ++x;
将以不同的语言给出不同的结果 看看下面的例子
int i = 5;
int x = i++;
cout << i << " " << x;
inti=5;
int x=i++;
库特
如何实现增量后的通用版本
我猜:T操作符++(int){T tmp(*this);++*this;返回tmp;}
如果我的类型不可复制,或者复制成本很高,该怎么办
嗯,我更喜欢:
Proxy operator++(int) { return Proxy(++*this, 1); }
然后有类似的事情:
bool operator==(Proxy const& left, T const& right) {
return left.value - 1 == right.value;
}
如果编译器可以自己复制并执行预增量,那么为什么语言中会出现后增量(重载)
因为您认为编译器可以进行复制是错误的,即使它可以保存,成本也可能太高。它们是两个独立的运算符,因为它们执行两个不同(尽管相关)的操作
预递增/递减将递增/递减变量,并返回新的值
int i = 0;
int j = ++i; // j is now 1
int i = 0;
int j = i++; // j is now 0
递增/递减后将递增/递减变量并返回旧值
int i = 0;
int j = ++i; // j is now 1
int i = 0;
int j = i++; // j is now 0
通常,这些运算符的实现如下所示(对于某些类型的T
):
如您所见,前缀重载不需要该类型的额外副本,而后缀版本需要
由于大部分评论都围绕着为什么会出现这种情况的问题:
简短的答案是:因为C标准是这样的(C++继承了C)。
较长的答案是:
++a
和a++
只是调用特定函数的简写符号++a
(对于给定类型T
)映射到T&T::operator++()
或T&operator++(T&)
,a++
映射到T::operator++(int)
或T operator(T&,int)
。与所有操作符一样,您(作为程序员)可以定义它们来执行与相应类型相关的任何操作(注意:通常认为重载操作符来执行奇怪的操作是不好的做法,但标准不会阻止您这样做)。通常,如果要定义类型(例如迭代器),可以通过提供类似的接口(例如重载适当的运算符),使其与内置类型(例如指针)的行为相匹配。但是,您可以决定希望运算符++()
执行二次公式,并希望运算符++(int)
进行傅里叶变换。因为它们是两个独立的函数,所以这是允许的。如果允许编译器根据将根据运算符++()
定义的前提推断运算符++(int)
,则它们将绑定在一起
<> C++中的运算符不过是函数调用的速记符号。虽然根据其他运算符实现多个运算符是很常见的,但标准并不要求这样做,因此编译器不能做出这样的假设。如果这是标准所要求的,那么会有很多假设行为需要跟踪
<>代码> <代码> +A+和 A++<代码>是C.的一个继承,存在大量代码,利用了一个或另一个的行为,并且改变C++中的代码将破坏与C的兼容性(除非你也在C标准中做了更改)。由于存在大量利用这些运算符行为的现有代码,因此您可能会做出重大的突破性更改
虽然在增量前实现增量后是很常见的,但您确实应该将这两个函数视为不同的函数(大致相同)
int x = 0;
int y = x++ + ++x;
*it++
struct iterator {
value_type value;
struct proxy {
value_type value;
value_type &operator*() { return value; }
value_type *operator->() { return &value; }
};
value_type &operator*() { return value; }
value_type *operator->() { return &value; }
iterator &operator++(); // actual increment code
proxy operator++(int) { proxy ret = { value }; ++*this; return ret; }
};
struct iterator {
value_type value;
bool needs_increment;
value_type &operator*() { if(needs_increment) ++*this; return value; }
value_type *operator->() { if(needs_increment) ++*this; return &value; }
iterator &operator++(); // actual increment code, resets needs_increment
value_type *operator++(int) { needs_increment = true; return &value; }
};