HTML Diff
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&amp;); SomeClass(SomeClass&amp;&amp;); SomeClass&amp; operator=(const SomeClass&amp;); SomeClass&amp; operator=(SomeClass&amp;&amp;); };<p>Если приглядеться, то можно заметить, что мы забыли указать спецификатор<strong>noexcept</strong>для перемещающих операций (либо не забыли, а не смогли реализовать их безопасно с точки зрения исключений).</p>
12 struct SomeClass { SomeClass(); ~SomeClass(); SomeClass(const SomeClass&amp;); SomeClass(SomeClass&amp;&amp;); SomeClass&amp; operator=(const SomeClass&amp;); SomeClass&amp; operator=(SomeClass&amp;&amp;); };<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