Несколько дней новогоднего волшебства:
Успейте начать обучение в 2018-ом году со скидкой до 30%!
Выбрать курс

Зачем что-то обещать или почему noexcept меняет поведение кода?

С++Deep_14.06_SITE.png

Если бы по телевизору показывали рекламу языка C++, наверняка, в ней бы было что-то вроде:

«С добавлением move-семантики вы получаете бесплатное увеличение производительности за счёт избавления от нецелесообразных операций копирования. Просто перекомпилируйте свой проект новой версией компилятора с поддержкой 11 или 14 стандарта и радуйтесь ускорению работы программы!».

Стоит ли верить такой рекламе?

Компилятор действительно может сгенерировать конструктор перемещения и оператор перемещающего присваивания. Это поможет избежать некоторых избыточных копирований. Однако в некоторых ситуациях компилятор не сможет выполнить генерацию. Перемещающие операции генерируются только в случае необходимости и если соблюдены следующие условия:

  1. В классе не объявлены никакие пользовательские копирующие операции (никаких конструкторов копирования и никаких операторов присваивания);
  2. В классе не объявлены никакие пользовательские операции перемещения (никаких конструкторов перемещения и никаких операторов перемещения);
  3. В классе не объявлен пользовательский деструктор.

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

Получим ли мы автоматическое ускорение старого кода, как обещано в рекламе?

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

struct SomeClass {

    SomeClass();
    ~SomeClass();

    SomeClass(const SomeClass&);
    SomeClass(SomeClass&&);

    SomeClass& operator=(const SomeClass&);
    SomeClass& operator=(SomeClass&&);

};

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

К чему это приведёт?

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

Рассчитывать на перемещение мы сможем только в тех случаях, когда в коде присутствует явная работа с r-value. Например, вот так:

SomeClass a;
SomeClass b(std::move(a));

А разве есть другие?

Да, есть и другие. И эти другие зачастую являются более желанными, чем явные случаи использовать перемещения. Примерами таких неявных случаев использования перемещения являются: алгоритмы, внутренние реалокации памяти с дальнейшим перемещением объектов. Если перемещающие операторы не помечены спецификатором noexcept, компилятор предпочтёт не рисковать и выберет копирующие операции.

Вывод

Использование спецификатора noexcept (или наоборот его неиспользование) может серьёзно изменить производительность вашей программы.

Есть вопросы? Напишите в комментариях!

Автор
0 комментариев
Для комментирования необходимо авторизоваться