Почему деструктор отключает генерацию неявных методов перемещения?

Я пытался понять, что говорит правило нуля, читая этот блог. IMO, он говорит, что если вы объявите свой собственный деструктор, то не забудьте сделать конструктор перемещения и переместить назначение по умолчанию.

пример:

class Widget {
public:
~Widget();         // temporary destructor
...                // no copy or move functions
};

«Добавление деструктора имеет побочный эффект отключения
генерация функций перемещения, но поскольку виджет можно копировать, все
код, который использовался для генерации ходов, теперь будет генерировать копии. В
Другими словами, добавление деструктора в класс вызвало
предположительно эффективные ходы, которые должны быть тихо заменены
предположительно менее эффективные копии «.

Приведенный выше текст Скотта Мейерса, внутри цитат, вызывает у меня некоторые вопросы:

  • Почему объявление деструктора скрывает семантику перемещения?
  • Объявление / определение деструктора скрывает только семантику перемещения или копирование
    конструктор и присвоение копии также скрывают семантику перемещения?

14

Решение

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

Управляет ли мой класс ресурсами?

Если это так, каждый ресурс должен быть перемещен в свой выделенный класс, чтобы ваши классы только управляли ресурсами (и ничего не делали) или только накапливали другие классы и / или выполняли те же логические задачи (но не управляли ресурсами).

Это частный случай более общего Принцип единой ответственности.

Когда вы примените его, вы сразу увидите, что для классов управления ресурсами вам придется определить конструктор перемещения вручную, назначение перемещения и деструктор (редко вам понадобятся операции копирования). А для нересурсных классов вам не нужно (и на самом деле вы, вероятно, не должны) объявлять что-либо из: переместить ctor / assignment, скопировать ctor / assignment, destructor.

Отсюда и «ноль» в названии: когда вы разделяете классы на управление ресурсами и другие, в «других» вам нужно предоставить ноль специальных функций-членов (они будут правильно сгенерированы автоматически).

В C ++ есть правила, согласно которым определение (специальной функции-члена) запрещает другие определения, но они только отвлекают вас от понимания сути правила ноля.

Для получения дополнительной информации см .:

  1. https://akrzemi1.wordpress.com/2015/09/08/special-member-functions/
  2. https://akrzemi1.wordpress.com/2015/09/11/declaring-the-move-constructor/
21

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

Почти всегда, если у вас есть деструктор (который «что-то делает»), вы должны следовать «правилу трех», которое затем становится «правилом пяти», если вы хотите переместить семантику.

Если ваш деструктор пуст, то он не нужен. Таким образом, подразумевается, что непустой деструктор (потому что у вас его не будет, если он не нужен!), Тогда вам также нужно делать то же самое в операциях копирования и присваивания, и, по-видимому, потребуется перенести конструкцию и присвоение перемещения «сделать что-то», а не просто передать фактический контент через.

Конечно, могут быть случаи, когда это не так, но компилятор использует подход «давайте применять автоматически сгенерированные функции перемещения, только если деструктор пуст», потому что это «безопасный» подход.

4

объявляет / определяет Dtor только скрыть семантику перемещения или копировать
ctor / copy назначение, а также скрыть семантику перемещения?

Если нет определенного пользователем переместить конструкторы предоставляются для класса, все следующее верно:

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

тогда компилятор объявит конструктор перемещения как неявный встроенный открытый член своего класса с signature T::T(T&&),

Таким образом, да объявление конструктора копирования или оператора присваивания также скрывает неявно объявленный конструктор перемещения.

1

Сначала я бы сказал, что ответ Матса Петерссона лучше принятого, поскольку в нем упоминается обоснование.

Во-вторых, в качестве дополнения я хочу уточнить немного больше.

Поведение неявно объявленного (или дефолтного) хода ctor

От C ++ проект:

Неявно определенный конструктор копирования / перемещения для класса X, не являющегося объединением, выполняет пошаговое копирование / перемещение своих баз и членов.

Условие, когда компилятор неявно объявляет ctor перемещения

От cppreference:

Если для класса не заданы пользовательские конструкторы перемещения, все
верно следующее:

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

тогда компилятор объявит конструктор перемещения как неявный встроенный открытый член своего класса с подписью T::T(T&&),

Почему dtor (и многие другие) мешает неявно объявленному перемещению ctor?

Если мы посмотрим на вышеприведенные условия, то не только объявленный пользователем деструктор предотвращает неявно объявленный ctor перемещения, объявленный пользователем конструктор копирования, объявленный пользователем оператор копирования и оператор присвоения перемещения — все имеет одинаковый эффект предотвращения.

Мэтс Петерссон указал, что обоснование таково:

Если компилятор считает, что вам может потребоваться сделать что-то другое, кроме членского перемещения в операции перемещения, тогда небезопасно предполагать, что вам это не нужно.

  • Когда есть деструкторы, объявленные пользователем, а это значит, что есть некоторая работа по очистке, тогда вы, вероятно, захотите сделать это с помощью переехала из объект.

  • Когда есть объявленные пользователем операторы присваивания перемещения, так как это также «движущиеся» ресурсы, вы, вероятно, захотите сделать то же самое в ctor перемещения.

  • Когда есть объявленные пользователем конструкторы копирования или операторы копирования, это наиболее интересный случай. Мы знаем это семантика перемещения позволяет нам сохранять семантику значений при одновременной оптимизации производительности, и этот ход «откатится» на копирование, когда ctor перемещения не предусмотрен. В каком-то смысле перемещение можно рассматривать как «оптимизированную копию». Поэтому, если операция копирования требует от нас что-то сделать, вполне вероятно, что аналогичная работа также необходима для операции перемещения.

Поскольку в описанных выше условиях может потребоваться что-то иное, чем членское перемещение, компилятор не будет считать, что вам это не нужно, и, следовательно, неявно объявляет ctor перемещения.

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