我最近阅读(并且遗憾地忘记了哪里),编写operator =的最佳方法是这样的:
foo &operator=(foo other)
{
swap(*this, other);
return *this;
}
而不是:
foo &operator=(const foo &other)
{
foo copy(other);
swap(*this, copy);
return *this;
}
这个想法是,如果使用rvalue调用operator =,则第一个版本可以优化复制的构造。因此,当使用rvalue调用时,第一个版本更快,当使用左值调用时,这两个版本是等效的。
我很好奇其他人对此的看法?由于缺乏明确性,人们会避免使用第一个版本吗?我是否正确,第一个版本可以更好,永远不会更糟?
答案 0 :(得分:4)
您可能会从以下网址阅读:http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
我没有太多话要说,因为我觉得这个链接很好地解释了这个理由。有趣的是,我可以确认第一个表单在使用MSVC的构建中产生的副本更少,这是有道理的,因为编译器可能无法在第二个表单上执行copy-elision。我同意第一种形式是严格的改进,并且永远不会比第二种形式更差。
编辑: 第一种形式可能不那么惯用,但我认为它不太清楚。 (IMO,这并不比第一次看到赋值运算符的复制和交换实现更令人惊讶。)
编辑#2:哎呀,我打算写copy-elision,而不是RVO。
答案 1 :(得分:2)
我通常更倾向于从可读性和“最不惊讶”的角度来看第二个,但是我确实认为当参数是临时的时,第一个可以更有效。
第一个真的可以导致 no 副本,而不仅仅是单个副本,并且可以想象在极端情况下这可能是一个真正的问题。
E.g。参加这个测试计划。 gcc -O3 -S
(gcc版本4.4.2 20091222(Red Hat 4.4.2-20)(GCC))生成对B的复制构造函数的一次调用,但没有调用函数f
的A的复制构造函数(赋值)运算符内联A
和B
)。 A
和B
都可以被视为非常基本的字符串类。 data
的分配和复制将在构造函数中发生,并在析构函数中释放。
#include <algorithm>
class A
{
public:
explicit A(const char*);
A& operator=(A val) { swap(val); return *this; }
void swap(A& other) { std::swap(data, other.data); }
A(const A&);
~A();
private:
const char* data;
};
class B
{
public:
explicit B(const char*);
B& operator=(const B& val) { B tmp(val); swap(tmp); return *this; }
void swap(B& other) { std::swap(data, other.data); }
B(const B&);
~B();
private:
const char* data;
};
void f(A& a, B& b)
{
a = A("Hello");
b = B("World");
}
答案 2 :(得分:0)
鉴于此
foo &foo::operator=(foo other) {/*...*/ return *this;}
foo f();
像这样的代码中的
foo bar;
bar = f();
编译器可能更容易消除对复制构造函数的调用。使用RVO,它可以使用运算符的other
参数的地址作为f()
构造其返回值的位置。
似乎这种优化对于第二种情况也是可能的,尽管我认为它可能更难。 (特别是当操作员没有内联时。)
答案 3 :(得分:-2)
这两个实际上是一样的。唯一的区别是你在调试器中按“Step In”。你应该提前知道在哪里做。
答案 4 :(得分:-3)
我认为你可能会混淆:
之间的区别 foo &operator=(const foo &other);
和
const foo &operator=(const foo &other);
第一个表格应该用于: (a = b) = c;