Я недавно читал (и к сожалению забыл, где), что лучший способ записать оператор = похож на это:
foo &operator=(foo other)
{
swap(*this, other);
return *this;
}
вместо этого:
foo &operator=(const foo &other)
{
foo copy(other);
swap(*this, copy);
return *this;
}
Идея состоит в том, что, если оператор = называют с rvalue, первая версия может оптимизировать далеко конструкцию копии. Таким образом, при вызове с rvalue первая версия быстрее и при вызове с lvalue эти два эквивалентны.
Мне любопытно относительно того, что другие люди думают об этом? Люди избежали бы первой версии из-за отсутствия явности? Я корректен, что первая версия может быть лучше и никогда не может быть хуже?
Вы, наверное, читали это по: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
Мне нечего сказать, так как мне кажется, что ссылка довольно хорошо объясняет логическое обоснование. Анекдотически я могу подтвердить, что первая форма приводит к меньшему количеству копий в моих сборках с MSVC, что имеет смысл, так как компиляторы могут быть не в состоянии сделать копирование на второй форме. Я согласен, что первая форма - это строгое улучшение и никогда не бывает хуже второй.
Правка: Первая форма может быть немного менее идиоматической, но я не думаю, что она намного менее ясна. (IMO, это не более удивительно, чем увидеть реализацию оператора присваивания в первый раз)
Edit #2: Oops, я имел в виду написание разрешения на копирование, а не RVO.
Учитывая это
foo &foo::operator=(foo other) {/*...*/ return *this;}
foo f();
в подобном коде
foo bar;
bar = f();
компилятору, возможно, будет проще устранить вызов конструктора копирования. С помощью RVO он мог бы использовать адрес параметра оператора other
в качестве места для f()
, чтобы построить его возвращаемое значение по этому адресу.
Кажется, что такая оптимизация возможна и для второго случая, хотя, на мой взгляд, это может быть сложнее. (Особенно, когда оператор не является инсценированным.)
.Эти двое на самом деле одни и те же. Единственная разница заключается в том, где вы нажимаете "Step In" в отладчике. И вы должны заранее знать, где это сделать.
Я думаю, что вы можете перепутать разницу между:
foo &operator=(const foo &other);
и
const foo &operator=(const foo &other);
Первая форма должна быть использована, чтобы разрешить: (a = b) = c;
Я, как правило, предпочитаю вторую из читаемости и «наименьшую точку зрения« минимум неожиданности », однако я признаю, что первый может быть более эффективным, когда параметр является временным.
Первый действительно может привести к NO копий копий, не только единую копию, и это возможно, что это может быть подлинной проблемой в экстремальных ситуациях.
E.G. Возьмите эту тестовую программу. GCC -O3 -S
(GCC версия 4.4.2 20091222 (RED HAT 4.4.2-20) (GCC)) генерирует один вызов COPTOR COPTOR BOPE, но нет вызовов к конструктору копирования копии для функции f
(оператор назначения активирован как для обоих A
и B
). А
и B
можно принимать, чтобы быть очень основными классами строки. Распределение и копирование для данных
будут происходить в конструкторах и дефинции в деструкторе.
#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");
}