0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: с++, оператор перемещающего присваивания, конструктор перемещения, перемещающие операции, конструктор копирования, оператор присваивания, оператор перемещения, пользовательский деструктор, спецификатор noexcept, r-value, внутренние реалокации памяти</p>
1
<p>Теги: с++, оператор перемещающего присваивания, конструктор перемещения, перемещающие операции, конструктор копирования, оператор присваивания, оператор перемещения, пользовательский деструктор, спецификатор noexcept, r-value, внутренние реалокации памяти</p>
2
<p>Если бы по телевизору показывали рекламу языка C++, наверняка, в ней бы было что-то вроде:</p>
2
<p>Если бы по телевизору показывали рекламу языка C++, наверняка, в ней бы было что-то вроде:</p>
3
<p><em>"С добавлением move-семантики вы получаете бесплатное увеличение производительности за счёт избавления от нецелесообразных операций копирования. Просто перекомпилируйте свой проект новой версией компилятора с поддержкой 11 или 14 стандарта и радуйтесь ускорению работы программы!".</em></p>
3
<p><em>"С добавлением move-семантики вы получаете бесплатное увеличение производительности за счёт избавления от нецелесообразных операций копирования. Просто перекомпилируйте свой проект новой версией компилятора с поддержкой 11 или 14 стандарта и радуйтесь ускорению работы программы!".</em></p>
4
<h2>Стоит ли верить такой рекламе?</h2>
4
<h2>Стоит ли верить такой рекламе?</h2>
5
<p>Компилятор действительно может сгенерировать конструктор перемещения и оператор перемещающего присваивания. Это поможет избежать некоторых избыточных копирований. Однако в некоторых ситуациях компилятор не сможет выполнить генерацию. Перемещающие операции генерируются только в случае необходимости и если соблюдены следующие условия:</p>
5
<p>Компилятор действительно может сгенерировать конструктор перемещения и оператор перемещающего присваивания. Это поможет избежать некоторых избыточных копирований. Однако в некоторых ситуациях компилятор не сможет выполнить генерацию. Перемещающие операции генерируются только в случае необходимости и если соблюдены следующие условия:</p>
6
<ol><li>В классе не объявлены никакие пользовательские копирующие операции (никаких конструкторов копирования и никаких операторов присваивания);</li>
6
<ol><li>В классе не объявлены никакие пользовательские копирующие операции (никаких конструкторов копирования и никаких операторов присваивания);</li>
7
<li>В классе не объявлены никакие пользовательские операции перемещения (никаких конструкторов перемещения и никаких операторов перемещения);</li>
7
<li>В классе не объявлены никакие пользовательские операции перемещения (никаких конструкторов перемещения и никаких операторов перемещения);</li>
8
<li>В классе не объявлен пользовательский деструктор.</li>
8
<li>В классе не объявлен пользовательский деструктор.</li>
9
</ol><p>Допустим, мы не сможем удовлетворить какое-нибудь из этих требований, и нам придётся реализовать, скажем, пользовательские операции копирования. Очевидно, тогда придётся определить и пользовательские операции перемещения.</p>
9
</ol><p>Допустим, мы не сможем удовлетворить какое-нибудь из этих требований, и нам придётся реализовать, скажем, пользовательские операции копирования. Очевидно, тогда придётся определить и пользовательские операции перемещения.</p>
10
<h2>Получим ли мы автоматическое ускорение старого кода, как обещано в рекламе?</h2>
10
<h2>Получим ли мы автоматическое ускорение старого кода, как обещано в рекламе?</h2>
11
<p>Предположим, мы взяли старый код и добавили к уже существующему (и используемому) классу операции копирования и перемещения. В итоге получилось что-то вроде следующего:</p>
11
<p>Предположим, мы взяли старый код и добавили к уже существующему (и используемому) классу операции копирования и перемещения. В итоге получилось что-то вроде следующего:</p>
12
struct SomeClass { SomeClass(); ~SomeClass(); SomeClass(const SomeClass&); SomeClass(SomeClass&&); SomeClass& operator=(const SomeClass&); SomeClass& operator=(SomeClass&&); };<p>Если приглядеться, то можно заметить, что мы забыли указать спецификатор<strong>noexcept</strong>для перемещающих операций (либо не забыли, а не смогли реализовать их безопасно с точки зрения исключений).</p>
12
struct SomeClass { SomeClass(); ~SomeClass(); SomeClass(const SomeClass&); SomeClass(SomeClass&&); SomeClass& operator=(const SomeClass&); SomeClass& operator=(SomeClass&&); };<p>Если приглядеться, то можно заметить, что мы забыли указать спецификатор<strong>noexcept</strong>для перемещающих операций (либо не забыли, а не смогли реализовать их безопасно с точки зрения исключений).</p>
13
<h2>К чему это приведёт?</h2>
13
<h2>К чему это приведёт?</h2>
14
<p>К тому, что мы будем сильно недовольны рекламой, ожидая везде, где только это возможно, перемещение, и получая копирование в некоторых случаях. Немного запутанно, но сейчас станет чуть понятнее.</p>
14
<p>К тому, что мы будем сильно недовольны рекламой, ожидая везде, где только это возможно, перемещение, и получая копирование в некоторых случаях. Немного запутанно, но сейчас станет чуть понятнее.</p>
15
<p>Рассчитывать на перемещение мы сможем только в тех случаях, когда в коде присутствует явная работа с<strong>r-value</strong>. Например, вот так: SomeClass a; SomeClass b(std::move(a));</p>
15
<p>Рассчитывать на перемещение мы сможем только в тех случаях, когда в коде присутствует явная работа с<strong>r-value</strong>. Например, вот так: SomeClass a; SomeClass b(std::move(a));</p>
16
<h2>А разве есть другие?</h2>
16
<h2>А разве есть другие?</h2>
17
<p>Да, есть и другие. И эти другие зачастую являются более желанными, чем явные случаи использовать перемещения. Примерами таких неявных случаев использования перемещения являются: алгоритмы, внутренние реалокации памяти с дальнейшим перемещением объектов. Если перемещающие операторы не помечены спецификатором<strong>noexcept</strong>, компилятор предпочтёт не рисковать и выберет копирующие операции.</p>
17
<p>Да, есть и другие. И эти другие зачастую являются более желанными, чем явные случаи использовать перемещения. Примерами таких неявных случаев использования перемещения являются: алгоритмы, внутренние реалокации памяти с дальнейшим перемещением объектов. Если перемещающие операторы не помечены спецификатором<strong>noexcept</strong>, компилятор предпочтёт не рисковать и выберет копирующие операции.</p>
18
<h2>Вывод</h2>
18
<h2>Вывод</h2>
19
<p>Использование спецификатора<strong>noexcept</strong>(или наоборот его неиспользование) может серьёзно изменить производительность вашей программы.</p>
19
<p>Использование спецификатора<strong>noexcept</strong>(или наоборот его неиспользование) может серьёзно изменить производительность вашей программы.</p>
20
<p><em>Есть вопросы? Напишите в комментариях!</em></p>
20
<p><em>Есть вопросы? Напишите в комментариях!</em></p>
21
21