Имеет ли смысл использовать идиому Move-and-Swap для подвижного и не копируемого класса - PullRequest
5 голосов
/ 26 июля 2011

Если у меня есть такой класс, как

class Foo{
public:
    Foo(){...}
    Foo(Foo && rhs){...}
    operator=(Foo rhs){ swap(*this, rhs);}
    void swap(Foo &rhs);
private:
    Foo(const Foo&);
// snip: swap code
};
void swap(Foo& lhs, Foo& rhs);

Имеет ли смысл реализовывать оператор = по значению и менять местами, если у меня нет конструктора копирования? Это должно предотвратить копирование моих объектов класса Foo, но разрешить перемещение.

Этот класс нельзя скопировать, поэтому я не смогу скопировать конструкцию или скопировать его.

Редактировать

Я проверил свой код с этим, и похоже, что я хочу поведение.

#include <utility>
#include <cstdlib>
using std::swap;
using std::move;
class Foo{
public: Foo():a(rand()),b(rand()) {}
        Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;}
        Foo& operator=(Foo rhs){swap(*this,rhs);return *this;}
        friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);}
private:
    //My compiler doesn't yet implement deleted constructor
    Foo(const Foo&);
private:
    int a, b;
};

Foo make_foo()
{
    //This is potentially much more complicated
    return Foo();
}

int main(int, char*[])
{
    Foo f1;
    Foo f2 = make_foo(); //move-construct
    f1 = make_foo(); //move-assign
    f2 = move(f1);
    Foo f3(move(f2));
    f2 = f3; // fails, can't copy-assign, this is wanted
    Foo f4(f3); // fails can't copy-construct

    return 0;
}

Ответы [ 3 ]

5 голосов
/ 26 июля 2011

Перемещение и обмен действительно разумны.Если вы отключите конструктор копирования, то единственный способ вызвать эту функцию - это создать аргумент с помощью конструктора перемещения.Это означает, что если вы напишите

lhs = rhs; // Assume rhs is an rvalue

, тогда конструктор аргумента в operator = будет инициализирован конструктором перемещения, опустошив rhs и установив аргумент в старое значение rhs.Вызов swap затем обменивает старое значение lhs и старое значение rhs, оставляя lhs со старым значением rhs.Наконец, деструктор для аргумента срабатывает, очищая старую память lhs.Как примечание, это действительно copy -and-swap, а не move -and-swap.

Тем не менее, то, что у вас есть сейчас 'не правильно.Реализация по умолчанию std::swap будет внутренне пытаться использовать конструктор перемещения для перемещения элементов, что приводит к бесконечному циклу рекурсии.Вы должны были бы перегрузить std::swap, чтобы заставить это работать правильно.

Вы можете увидеть это онлайн здесь на ideone .

Для получения дополнительной информации см. этот вопрос и обсуждение «правила четырех с половиной».

Надеюсь, это поможет!

2 голосов
/ 26 июля 2011

Я думаю, что это хорошо, но я не совсем понимаю, почему вы просто не сделаете:

operator=(Foo&& rhs) // pass by rvalue reference not value

И сохраните себе ход.

1 голос
/ 26 июля 2011

Что следует за мнением, и я не очень разбираюсь в стандарте 0x, но я думаю, что у меня есть достаточно веские аргументы в поддержку.

Нет. На самом деле было бы уместно вообще не поддерживать присвоение.

Рассмотрим семантику:

«назначить» означает «причина B, которая уже существует,идентичен ".«копировать» означает «создать B и сделать его идентичным A».«своп» означает «заставить B быть идентичным тому, что было A, и одновременно заставить A быть идентичным тому, что было B».«двигаться» означает «заставить B быть идентичным тому, что было A, и уничтожить A».

Если мы не можем копировать, то мы не можем копировать и менять местами.Копирование и замена предназначены для безопасного выполнения присваивания: мы создаем C, идентичный A, меняем его на B (так что теперь C - это то же, что B, а B - A) и уничтожаем C(очистка старых данных B).Это просто не работает с перемещением и обменом: мы не должны уничтожать А в любой точке, но движение уничтожит его.Кроме того, перемещение не создает нового значения, поэтому происходит то, что мы перемещаем A в B, и тогда нам не с чем поменяться.

Кроме того, причина - сделать класс некопируемым, безусловно, не потому, что«Создать B» будет проблематично, но потому что «заставить его быть идентичным A» будет проблематичным.Итак, если мы не можем скопировать, почему мы должны ожидать, что сможем назначить?

...