C++, почему не требуется никаких исключений в контексте конструкторов перемещения и операторов назначения перемещения для включения оптимизации?

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

class my_class
{

    protected:

    double *my_data;
    uint64_t my_data_length;
}

my_class(my_class&& other) noexcept : my_data_length{other.my_data_length}, my_data{other.my_data}
{
    // Steal the data
    other.my_data = nullptr;
    other.my_data_length = 0;
}

const my_class& operator=(my_class&& other) noexcept
{
    // Steal the data
    std::swap(my_data_length, other.my_data_length);
    std::swap(my_data, other.my_data);

    return *this;
}

Какова цель noexcept Вот? Я знаю, что это хиты для компилятора, что никакие исключения не должны создаваться следующей функцией, но как это позволяет оптимизировать компилятор?

3 ответа

Особая важность noexcept конструкторы перемещения и операторы присваивания подробно описаны в https://vimeo.com/channels/ndc2014/97337253

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

Например, когда вы делаете push_back(t) на векторе, если вектор полон (size() == capacity()) затем необходимо выделить новый блок памяти и скопировать все существующие элементы в новую память. Если при копировании любого из элементов возникает исключение, то библиотека просто уничтожает все элементы, созданные в новом хранилище, и освобождает новую память, оставляя исходный вектор неизменным (что соответствует строгой гарантии безопасности исключений). Было бы быстрее переместить существующие элементы в новое хранилище, но если бы перемещение могло привести к выбросу, то любые уже перемещенные элементы были бы уже изменены, и выполнение строгой гарантии было бы невозможным, поэтому библиотека будет пытаться переместить их только тогда, когда он знает, что не может бросить, что он может знать только, если они noexcept,

ИМХО используя noexcept не будет включать какую-либо оптимизацию компилятора самостоятельно. В STL есть черты:

std::is_nothrow_move_constructible
std::is_nothrow_move_assignable

STL контейнеры, такие как vector и т.д. используйте эти черты для тестирования типа T и используйте конструкторы перемещения и присваивания вместо конструкторов копирования и присваивания.

Почему STL использует эти черты вместо:

std::is_move_constructible
std::is_move_assignable

Ответ: обеспечить сильную гарантию исключения.

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

Оптимизация может быть выполнена несколькими различными способами. Автоматически компилятором, а также различными реализациями кода, который использует ваши конструкторы и оператор присваивания. Взгляните на STL, есть некоторые специализации для кода, которые отличаются, если вы используете исключения или нет, которые реализуются через черты типа.

Сам компилятор может оптимизировать лучше, имея гарантию того, что ни один код никогда не генерирует. У вашего компилятора есть гарантированное дерево вызовов, которое может быть лучше встроено, рассчитано время компиляции или что-то еще. Минимальная оптимизация, которая может быть сделана, состоит в том, чтобы не хранить всю информацию о фактическом кадре стека, которая необходима для обработки условия выброса, например, переменные освобождения в стеке и другие вещи.

Здесь также возник вопрос: noexcept, разматывание стека и производительность

Может быть, ваш вопрос является дубликатом к этому?

Может быть, полезный вопрос, связанный с этим, я нашел здесь: Требуются ли конструкторы перемещения, кроме? Это обсудить необходимость бросать в ход операции.

Какова цель noexcept здесь?

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

Другие вопросы по тегам