以前的情况是预增量是首选的,因为类上的重载后增量需要返回表示增量前对象状态的临时副本.
似乎这不再是一个严重的问题(只要内联就位),因为我的旧C++编译器(GCC 4.4.7)似乎将以下两个函数优化为相同的代码:
class Int {
//...
public:
Int (int x = 0);
Int & operator ++ ();
Int operator ++ (int) {
Int x(*this);
++*this;
return x;
}
};
Int & test_pre (Int &a) {
++a;
return a;
}
Int & test_post (Int &a) {
a++;
return a;
}
Run Code Online (Sandbox Code Playgroud)
两个函数的结果汇编是:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdi, %rbx
call _ZN3IntppEv
movq %rbx, %rax
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
然而,如果没有任何内联,那么优先选择预增量到后增量似乎仍然有好处,因为它
test_post被强制调用operator++(int).
我们假设operator++(int)内联为惯用的复制构造函数,调用预增量,并返回副本,如上所示.如果拷贝构造函数被内联或默认拷贝构造函数的实现,是有足够的信息,编译器优化后递增,这样test_pre并test_post成为相同的功能?如果没有,还需要哪些其他信息?
Naw*_*waz 16
是.内置类型无关紧要.对于这样的类型,编译器可以轻松地分析语义并优化它们 - 如果这不会改变行为.
然而,对于类类型,它可能(如果不是做)的事情,因为语义可能是在这种情况下更复杂.
class X { /* code */ };
X x;
++x;
x++;
Run Code Online (Sandbox Code Playgroud)
最后两个调用可能完全不同,可能会执行不同的操作,就像这些调用一样:
x.decrement(); //may be same as ++x (cheating is legal in C++ world!)
x.increment(); //may be same as x++
Run Code Online (Sandbox Code Playgroud)
所以不要让自己被困在语法糖中.
通常,用户定义类型中的后增量运算符涉及创建比典型的预增量运算符更慢且更昂贵的副本.
因此,应优先使用预增量运算符,而不是用户定义的类型.
同样是一致的好风格,因此内置类型也应该首选预增量.
例:
struct test
{
// faster pre-increment
test& operator++() // pre-increment
{
// update internal state
return *this; // return this
}
// slower post-increment
test operator++(int)
{
test c = (*this); // make a copy
++(*this); // pre-increment this object
return c; // return the un-incremented copy
}
};
Run Code Online (Sandbox Code Playgroud)
不能期望编译器优化用户定义类型的后递增,因为它们的实现是约定,而不是编译器可以推断的.