Конструктор / назначение перемещения по умолчанию и конструктор / назначение удаленной копии

Согласно стандарту,

Если определение класса X явно не объявляет конструктор перемещения, он будет неявно объявлен как дефолтный, если и только если

— X не имеет объявленного пользователем конструктора копирования,

— X не имеет заявленного пользователем оператора копирования,

— X не имеет объявленного пользователем оператора назначения перемещения, и

— X не имеет объявленного пользователем деструктора.

Теперь следующее не компилируется

# include <utility>

class Foo
{
public:
Foo() = default;
Foo(Foo const &) = delete;
};

int main()
{
Foo f;
Foo g(std::move(f)); // compilation fails here
return 0;
}

Таким образом, кажется, что удаленная функция считается пользовательской, что имеет смысл (это не реализация по умолчанию). Тем не менее, в этом конкретном случае, как бы конструктор / назначение по умолчанию перемещал удаленную копию construtor / assignment mess?

Я думаю, что этот вопрос имеет практическое значение, потому что ручная генерация и ESP. поддержание таких функций по умолчанию подвержено ошибкам, в то же время (праведное) увеличение использования таких классов, как std::unique_ptr поскольку участники класса сделали не копируемые классы намного более обычными животными, чем раньше.

12

Решение

user-declared означает либо предоставленный пользователем (определенный пользователем), явно дефолт (= default) или же явно удален (= delete) в отличие от неявно заданного по умолчанию / удаленного (например, вашего конструктора перемещения).

Так что в вашем случае, да конструктор перемещения косвенным образом удален, потому что конструктор копирования эксплицитно удален (и, таким образом, пользователь объявлена).

Тем не менее, в этом конкретном случае, как удалить конструктор / назначение удаленного копирования конструктор / назначение присваивания по умолчанию?

Это не так, но стандарт не делает различий между этим делом и сложным.

Самый короткий ответ заключается в том, что имея косвенным образом определенный Move-конструктор с эксплицитно удаленный экземпляр-конструктор может быть быть опасным в некоторых случаях, то же самое, когда у вас есть определяемые пользователем деструктор и нет определяемые пользователем конструктор копирования (см. Правило трех / пяти / нуля). Теперь вы можете утверждать, что пользовательский деструктор не удаляет конструктор копирования, но это просто изъян на языке, который нельзя удалить, потому что это сломало бы много старых (плохих) программ. Процитирую Бьярне Страуструпа:

В идеальном мире, я думаю, мы бы выбрали «нет генерации» по умолчанию и предоставили бы действительно простую запись «дайте мне все обычные операции». […] Кроме того, политика «без операций по умолчанию» приводит ошибки времени компиляции (которые мы должны легко исправить), в то время как политика генерации операций по умолчанию приводит к проблемам, которые невозможно обнаружить до времени выполнения.

Вы можете прочитать больше об этом в N3174 = 10-0164.

Обратите внимание, что большинство людей следуют Правило трех / пяти / нуля, и, по моему мнению, вы должны. Неявным образом удаляя конструктор перемещения по умолчанию, стандарт «защищает» вас от ошибок и должен был защищать вас долгое время, удаляя конструктор копирования в некоторых случаях (см. Статью Бьярне).

Дальнейшее чтение, если вы заинтересованы:

Я думаю, что этот вопрос имеет практическое значение, потому что ручная генерация и ESP. поддержание таких функций по умолчанию подвержено ошибкам, в то же время (праведное) увеличение использования таких классов, как std::unique_ptr поскольку участники класса сделали не копируемые классы намного более обычными животными, чем раньше.

Пометка конструктора перемещения как явного значения по умолчанию решит эту проблему:

class Foo {
public:
Foo() = default;
Foo(Foo const &) = delete;
Foo(Foo&&) = default;
};

Вы получаете не копируемый объект с конструктором перемещения по умолчанию, и, по моему мнению, эти явные объявления лучше, чем неявные (например, только объявив конструктор перемещения как default без удаления копи-конструктора).

18

Другие решения

Как вы заявили, из §12.8

Если определение класса X явно не объявляет конструктор перемещения, он будет неявно объявлен
по умолчанию, если и только если

  • Х не имеет пользователь объявлена конструктор копирования,

  • […]

Обратите внимание пользователь объявлена. Но если вы посмотрите на §8.4.3:

Определение функции формы:

Атрибут спецификатор-слвыбирать Децл-спецификатор-слвыбирать декларатор virt-specier-seqвыбирать = удалить;

называется удаленным определение. Функция с удаленным определение также называется удаленной функцией.

Программа, которая ссылается на удаленную функцию неявно или явно, кроме объявить это, плохо сформирован.

Таким образом, стандарт определяет deleteD функционирует как пользователь объявлена (как вы можете видеть сверху), даже если они deleteг и не может быть использован.

Тогда, согласно §12.8, неявный конструктор перемещения не определен из-за объявленного пользователем (с = delete;) конструктор копирования.

2

Поведение может быть по существу проиллюстрировано с помощью:

void foo(const int& i)
{
std::cout << "copy";
}

void foo(int&& i)
{
std::cout << "move";
}

При наличии обеих перегрузок int&& Перегрузка выбрана для значений, в то время как const int& Перегрузка выбрана для lvalues. Если вы удалите int&& перегрузки, const int& называется (эрго, не ошибка) даже для значений. По сути, это то, что происходит в этом случае:

class Foo
{
public:
Foo() = default;
Foo(Foo const &) = delete;
};

Разрешение перегрузки видит только одного кандидата, но, поскольку оно явно удалено, программа некорректна.

Ненормативная записка под цитируемым вами разделом поясняет, что именно так и происходит:

[ Заметка: Когда конструктор перемещения не объявлен явно или не предоставлен явно, выражения, которые в противном случае вызвали бы
Конструктор перемещения может вместо этого вызвать конструктор копирования. — конечная нота
]

-1
По вопросам рекламы [email protected]