Подтвердить что ты не робот

Конструктор перемещения по умолчанию против конструктора копирования по умолчанию против оператора присваивания по умолчанию

Почему компилятор С++ имеет больше ограничений на автоматически создаваемые конструкторы перемещения, чем на автоматически созданный конструктор копий или оператор присваивания?

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

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

Интересно, почему разница.

4b9b3361

Ответ 1

Я считаю, что обратная совместимость здесь играет большую роль. Если пользователь определяет любую функцию "Правило трех" (копирование ctor, назначение копирования op, dtor), можно предположить, что класс выполняет некоторое внутреннее управление ресурсами. Неявное определение конструктора перемещения может внезапно сделать класс недействительным при компиляции под С++ 11.

Рассмотрим следующий пример:

class Res
{
  int *data;

public:
  Res() : data(new int) {}

  Res(const Res &arg) : data(new int(*arg.data)) {}

  ~Res() { delete data; }
};

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

Что касается оператора присваивания перемещения, предотвращающего определения конструктора перемещения по умолчанию: если оператор присваивания перемещения выполняет что-то другое, чем значение по умолчанию, скорее всего, было бы неправильно использовать конструктор перемещения по умолчанию. Это просто "Правило трех" / "Правило пяти" по сути.

Ответ 2

Насколько я знаю, это из-за нисходящей совместимости. Рассмотрим классы, написанные на С++ (до С++ 11), и что произойдет, если С++ 11 начнет автоматически генерировать перемещение параллельно с существующими копировальными машинами или вообще любым другим ctor. Это легко сломало бы существующий код, минуя копию-ctor, которую написал автор этого класса. Следовательно, правила для создания движка-ctor, где создаются только для "безопасных" случаев.

Здесь статья Дейва Абрахама о почему неявный ход должен идти, что в конечном итоге привело к текущим правилам С++ 11.

И это пример того, как это произойдет:

// NOTE: This example assumes an implicitly generated move-ctor

class X
{
private:    
    std::vector<int> v;

public:
    // invariant: v.size() == 5
    X() : v(5) {}

    ~X()
    {
        std::cout << v[0] << std::endl;
    }
};

int main()
{
    std::vector<X> y;

    // and here is where it would fail:
    // X() is an rvalue: copied in C++03, moved in C++0x
    // the classes' invariant breaks and the dtor will illegally access v[0].
    y.push_back(X());
}

Ответ 3

Когда С++ был создан, было решено, что конструктор по умолчанию, копировальный конструктор, оператор присваивания и деструктор будут сгенерированы автоматически (если не предусмотрено). Зачем? Поскольку компиляторы С++ должны иметь возможность скомпилировать (наиболее) C-код с одинаковой семантикой и что как struct работать в C.

Однако позже было замечено, что всякий раз, когда пользователь пишет пользовательский деструктор, ей, вероятно, также нужно написать собственный экземпляр-конструктор/оператор-присваивание; это называется Правило Большой тройки. Оглядываясь назад, мы можем видеть, что можно было бы указать, что созданный экземпляр-конструктор/присваивание-оператор/деструктор был бы только сгенерирован, если ни один из 3 не был предоставлен пользователем, и это помогло бы уловить множество ошибок... и по-прежнему сохраняют обратную совместимость с C.

Поэтому, когда С++ 11 пришел, было решено, что на этот раз все будет сделано правильно: новый оператор move-constructor и оператор move-assign-only будет генерироваться автоматически, если было бы ясно, что пользователь не был делая что-то "особенное" с классом. Любое "специальное" определяется как переопределение поведения перемещения/копирования/уничтожения.

Чтобы помочь в этом случае, люди будут делать что-то особенное, но все же хотят "автоматически сгенерированные" специальные методы, добавлено сахаросодержание = default.

К сожалению, по соображениям обратной совместимости комитет С++ не смог вернуться вовремя и изменить правила автоматической генерации для копирования; Мне жаль, что они не устарели, чтобы проложить путь к следующей версии Стандарта, но я сомневаюсь, что они будут., однако, он устарел (см. §12.8/7 для конструктора копирования, например, любезно предоставлен @Nevin).