#include<iostream>
using namespace std;
class X
{
int i;
public:
X(int a=0) : i(a) {}
friend X operator+ (const X& left,const X&right);
};
X operator+ (const X& left,const X&right) // Method 1
{
return X(left.i + right.i);
}
X operator+ (const X& left,const X&right) // Method 2
{
X temp(left.i + right.i);
return temp;
}
int main()
{
X a(2),b(3),c;
c=a+b;
c.print();
return 0;
}
在此代码中,Operator +通过2种不同的方法重载。
我的问题是这些方法之间有什么区别,应该考虑哪些方法更实用?
答案 0 :(得分:6)
我看不出任何编译器会在这两个版本之间生成不同代码的情况。第二个稍微冗长一点,但在这种情况下允许编译器优化掉名义上的额外副本,我不知道任何编译器不会做那个省略。
那就是说,这是一个微优化:编写最清晰的代码,然后将我带到最后一点。不要编写这些运算符中的任何一个,而是将惯用版本与+=
:
X& operator+=(const X&right) { i += right.i; return *this; }
X operator+(X left, const X& right) { return left += right; }
答案 1 :(得分:3)
这两种方法之间没有区别,您应该使用最符合您意图的方法。
copy elision 的第12.8 / 31段规定:
当满足某些条件时,允许实现省略类的复制/移动构造 即使为复制/移动操作选择的构造函数和/或对象的析构函数也是如此 有副作用。在这种情况下,实现处理省略的复制/移动的源和目标 操作只是指向同一对象的两种不同方式,以及对该对象的破坏 发生在两个对象在没有优化的情况下被销毁的时间的晚些时候。 复制/移动操作的省略,称为复制省略,在以下情况下是允许的(其中 可以合并以消除多个副本):
- 在具有类返回类型的函数的
return
语句中,当表达式是a的名称时 具有相同cv-unqualified的非易失性自动对象(函数或catch子句参数除外) 键入函数返回类型,可以通过构造省略复制/移动操作 自动对象直接进入函数的返回值- [...]
正如您所看到的,创建临时和 id-expression 命名具有自动存储持续时间的本地对象都有资格进行复制省略。
此外,编译器会将本地temp
视为 rvalue (读取:在本例中类似于临时),以便从函数返回它。 C ++ 11标准第12.8 / 32段规定:
当满足或将满足复制操作的省略标准时,除了源的事实 object是一个函数参数,要复制的对象由左值,重载决策指定 选择首先执行复制的构造函数,就好像对象是由右值指定的一样。 [...]
因此,我强烈建议从返回类型中移除 const
限定条件:
const X operator + (const X& left, const X&right)
// ^^^^^
// Don't use this!
在C ++ 11中,这将禁止移动语义,因为你不能从const
对象移动,即使它是一个rvalue - 简而言之,X
的移动构造函数,提供一个存在,不会被选中,并且将调用复制构造函数。
答案 2 :(得分:2)
区别在于方法1使用了一个名为“返回值优化”的概念。
方法1:
当编译器发现你没有使用它正在创建的对象而不是返回它。编译器利用了这一点,“它直接在应该返回该值的位置构建对象”。这里,只需要一个普通的构造函数调用(不需要复制构造函数),也没有析构函数调用,因为你从未真正创建过本地对象。这样效率更高。
方法2:
创建名为temp的第一个临时对象。然后复制构造函数将temp复制到oustide返回值的位置。 然后在范围结束时为析构函数调用temp。
最终方法1效率更高,但这是编译器相关的功能。
答案 3 :(得分:0)
第二个实现将导致NRV优化。 Stan Lippman说NRV优化需要一个显式的复制构造函数,但这里的类X很简单,所以我不认为NRV需要显式的复制构造函数。