C++ 为什么前inc和后inc/减量操作符是分开实现的?

C++ 为什么前inc和后inc/减量操作符是分开实现的?,c++,operator-overloading,C++,Operator Overloading,我试图找到增量运算符和减量运算符的re版本和post版本分别可重载的基本原理。 在我看来,在我所见过的任何类型的类的每个实现中,这些运算符都是相同的运算符(=做相同的事情),只是在调用时有所不同。 我认为,C++的设计者会有一个代码> +++/COD>运算符,编译器会根据需要在读取值之前或之后调用它(或者,更可能的是,在前一个或下一个序列点,我认为是等价的)。 因此,问题是:是否有人有一个案例/类的例子,其中这些案例/类的实现可能不同?或者有人知道/猜测这种设计选择背后的基本原理吗 对于那些喜

我试图找到增量运算符和减量运算符的re版本和post版本分别可重载的基本原理。
在我看来,在我所见过的任何类型的类的每个实现中,这些运算符都是相同的运算符(=做相同的事情),只是在调用时有所不同。
我认为,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; }
};