TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #1107 4.03K
Когда компилятор не сгенерирует 5 специальных методов?
#опытным

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

Вам в любом случае стоит придерживаться правил 0 и 5, когда проектируете интерфейс создания и уничтожения объектов. Но все равно полезно знать, что будет если правила не выполнять.

Так когда же компилятор не будет генерировать каждый из специальных методов? Пост в первую очередь про взаимосвязь специальных методов, другие причины упоминаться не будут.

1️⃣ Деструктор

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

Банальный пример:

struct Bad {
Bad() : p{new int{5}} {}
Bad(Bad&& other) {
p = other.p;
other.p = nullptr;
}
int * p;
};

{
Bad b;
} // memory leak


Очевидно, что мы по коду по-особенному управляем указателем. Но компилятор все равно сам сгенерирует деструктор, который ничего не освободит и мы получим утечку памяти.

2️⃣ Конструктор копирования и копирующий оператор присваивания

Если в классе определен хотя бы один перемещающий специальный метод, то ни один копирующий метод не генерируется.

struct Example {
Example() = default;
Example(Example&&) {}
};

Example a;
Example b = a; // Error: copy ctor is implicitly declared as deleted
Example c;
c = a; // Error: copy assign operator is implicitly declared as deleted


Но это не потому что компилятор такой вредный. Если вы своими ручками определили перемещающие операции, но не определили копирующие, то вы скорее всего и не хотите, чтобы объекты можно было копировать. Но даже если и хотите, то компилятор уже понимает, что поверхностное копирование полей вам не подойдет и просто предостерегает вас от проблем.

При этом если вы определили деструктор, то копирующий операции все равно сгенерируются.

struct Bad {
Bad() : p{new int{5}} {}
~Bad() {delete p;}
int * p;
};

{
Bad b;
Bad b1 = b;
} // double free


Простейший пример и сразу же ловим двойное освобождение.
Такое поведение - наследие от более ранних стандартов, когда было правило 3-х, но оно было только на словах. Даже если деструктор делает нетривиальные вещи, то копирующие операции генерировались.

Конечно, это unsafe. Но обратная совместимость заставляет С++ нести эти особенности в новые стандарты.

3️⃣ Конструктор перемещения и перемещающее присваивание

Это те 2 новых специальных метода, которые добавили в С++11. Для новых вещей стандарт может устанавливать новые условия, которые не несут груз ответственности за обратную совместимость.

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

struct Example {
~Example() {}
// or Example(Example&& other) {}
// or Example& operator=(const Example&) {}
// or Example& operator=(Example&&) {}
};

Example a;
Example b;
a = std::move(b); // Error: no move assign


Оно и понятно: если вы что-то сами определяете, значит хотите чего-то особенного. В этом плане компилятор усиливает правило 5: теперь вы обязаны сами определить перемещающие операций, если определяете другие специальные методы.

Be special. Stay cool.

#cpp11
  • 👍 15
  • ❤ 8
  • 🔥 7
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →