Ответ 1
Руководство
Никогда не удаляйте специальные элементы перемещения.
В типичном коде (например, в вашем вопросе) есть две причины для удаления участников перемещения. Одна из этих мотивов порождает неверный код (как в вашем примере), а для другой мотивации удаление элементов перемещения является избыточным (не наносит никакого вреда и не хорош).
-
Если у вас есть класс для копирования и вы не хотите перемещать элементы, просто не объявляйте их (в том числе не удаляя их). Удаленные участники все еще объявлены. Удаленные участники участвуют в разрешении перегрузки. Члены, которых нет, не делают. Когда вы создаете класс с допустимым конструктором копирования и удаленным элементом перемещения, вы не можете вернуть его по значению из функции, потому что разрешение перегрузки будет связано с удаленным элементом перемещения.
-
Иногда люди хотят сказать: этот класс не движется и не копируется. Правильно удалить и копию, и элементы перемещения. Однако достаточно просто удалить элементы копии (пока элементы перемещения не объявлены). Объявленные (даже удаленные) члены-копии запрещают компилятору объявлять элементы перемещения. Поэтому в этом случае элементы удаленного перемещения просто избыточны.
Если вы объявляете удаленные элементы перемещения, даже если вам удастся выбрать случай, когда он является избыточным и не является неправильным, каждый раз, когда кто-то читает ваш код, им нужно вновь обнаружить, является ли ваш случай избыточным или неправильным. Сделайте это проще для читателей вашего кода и никогда не удаляйте элементы перемещения.
Неверный случай:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
// ...
};
Вот пример кода, который вы, вероятно, ожидаете работать с CopyableButNotMovble
, но не будет работать во время компиляции:
CopyableButNotMovble
get()
{
CopyableButNotMovble x;
return x;
}
int
main()
{
CopyableButNotMovble x = get();
}
test.cpp:22:26: error: call to deleted constructor of 'CopyableButNotMovble'
CopyableButNotMovble x = get();
^ ~~~~~
test.cpp:7:5: note: 'CopyableButNotMovble' has been explicitly marked deleted here
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
^
1 error generated.
Правильный способ сделать это:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
// ...
};
Резервный случай:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble(NeitherCopyableNorMovble&&) = delete;
NeitherCopyableNorMovble& operator=(NeitherCopyableNorMovble&&) = delete;
// ...
};
Более читаемый способ сделать это:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
// ...
};
Это помогает, если вы практикуете группировку всех 6 ваших специальных членов в верхней части объявления класса, в том же порядке, пропуская те, которые вы не хотите объявлять. Эта практика облегчает читателям вашего кода быстрое определение того, что вы специально не объявили какого-либо особого участника.
Например, вот следующий шаблон:
class X
{
// data members:
public:
// special members
~X();
X();
X(const X&);
X& operator=(const X&);
X(X&&);
X& operator=(X&&);
// Constructors
// ...
};