Является ли специализация 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);
    }
};

независимо от реализации конструктора перемещения и оператора присваивания.