Является ли специализация std:: swap устаревшей теперь, когда у нас есть семантика перемещения?
Возможный дубликат:
Переместить семантику == пользовательская функция свопинга устарела?
Вот как выглядит std::swap
в С++ 11:
template<typename T>
void swap(T& x, T& y)
{
T z = std::move(x);
x = std::move(y);
y = std::move(z);
}
Должен ли я по-прежнему специализироваться на std::swap
для моих собственных типов или будет std::swap
быть таким же эффективным, как и он, при условии, что мой класс определяет конструктор перемещения и оператор назначения перемещения, конечно?
Ответы
Ответ 1
Специализация std::swap
теперь необязательна, но не устарела. Обоснование - это производительность.
Для кода прототипирования и, возможно, даже для большого кода доставки std::swap
будет достаточно быстро. Однако, если вы находитесь в ситуации, когда вам нужно извлечь все немного из вашего кода, писать пользовательский своп все равно может быть значительным преимуществом производительности.
Рассмотрим случай, когда ваш класс по существу имеет один владеющий указатель, а ваш конструктор перемещения и перемещение назначения просто должны иметь дело с этим одним указателем. Считайте загрузочные и запоминающие устройства для каждого элемента:
Переместить конструктор: 1 загрузка и 2 магазина.
Переместить назначение: 2 загрузки и 2 магазина.
Пользовательский своп: 2 загрузки и 2 магазина.
std::swap
- 1 конструкция перемещения и 2 перемещения, или: 5 нагрузок и 6 магазинов.
Пользовательский своп потенциально все еще в два-три раза быстрее, чем std::swap
. Хотя каждый раз, когда вы пытаетесь выяснить скорость чего-либо, посчитав грузы и магазины, обе будут злобными быстро.
Примечание. При вычислении стоимости вашего назначения перехода убедитесь в том, что вы будете перемещаться в значение с перемещением (в алгоритме std::swap
). Это часто отрицает стоимость освобождения, хотя и за счет ветки.
Ответ 2
Специализируется std:: swap теперь не рекомендуется, если у нас есть семантика перемещения?
Нет. Это универсальная версия, но вы можете оптимизировать ее, чтобы пропустить третью операцию перемещения. Мое предпочтение состоит в том, чтобы комбинировать copy & swap idiom с настройкой std:: swap для моих классов.
Это означает, что у меня будет:
class Aaaa
{
public:
Aaaa(); // not interesting; defined elsewhere
Aaaa(Aaaa&& rvalueRef); // same
Aaaa(const Aaaa& ref); // same
~Aaaa(); // same
Aaaa& operator=(Aaaa object) // copy&swap
{
swap(object);
return *this;
}
void swap(Aaaa& other)
{
std::swap(dataMember1, other.dataMember1);
std::swap(dataMember2, other.dataMember2);
// ...
}
// ...
};
namespace std
{
template<> inline void std::swap(Aaaa& left, Aaaa& right)
{ left.swap(right); }
}
Ответ 3
Это будет зависеть от ваших типов.
Вы переместите его от x до z, от y до x, от z до y. Это три операции копирования базового представления (может быть, только один указатель, может быть, что-то еще, кто знает)
Теперь, возможно, вы можете создать более быструю свопинг для своего типа (xor swap трюк, встроенный ассемблер или, может быть, std:: swap для ваших базовых типов только быстрее).
Или, может быть, ваш компилятор хорош в оптимизации и по существу оптимизирует оба случая в тех же инструкциях (например, временные в регистре).
Я лично стараюсь всегда выполнять функцию swap-члена, которая будет вызываться из нескольких мест, включая такие вещи, как назначение перемещения, но YMMV.
Ответ 4
Этот swap()
вызывает конструктор перемещения и 2 назначения перемещения. Я думаю, что можно написать более эффективный swap()
для его определенного типа класса, например,
class X
{
int * ptr_to_huge_array;
public:
// ctors, assgn ops. etc. etc.
friend void swap(X& a, X& b)
{
using std::swap;
swap(a.ptr_to_huge_array, b.ptr_to_huge_array);
}
};
независимо от реализации конструктора перемещения и оператора присваивания.