Как использовать noexcept в операторе присваивания с идиомой копирования и свопинга?

Оператор присваивания переходов часто должен быть объявлен noexcept (т.е. хранить тип в контейнерах STL). Но идиома "копирование и своп" позволяет определять как операторы присваивания копий, так и операции перемещения в одном фрагменте кода. Что делать с noexcept спецификатором в этом случае? Конструкция копии может быть брошена, но я сомневаюсь, может ли она нарушить спецификатор noexcept.

// Is it correct considering that T copy constructor can throw?
T& operator=(T other) noexcept;

Ответ 1

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

Единственное, что вы можете сделать, это безопасное воспроизведение и добавление обоих параметров в вашу спецификацию noexcept. Конечно, это означает, что вы получаете некоторые ложные отрицания.

Ответ 2

Как обычно, Даниэль Фрей правильный. Все, что мне нужно, это показать фрагмент кода, который иллюстрирует точку.

#include <iostream>

struct foo {

    foo() = default;

    foo(const foo&) {
        std::cout << "throw\n";
        throw 1;
    }

    foo& operator =(foo) noexcept {
        return *this;
    }
};

int main() {

    foo f, g;
    try {
        f = g; // throws
    }
    catch(int) {
        std::cout << "catch\n";
    }
}

При компиляции с gcc 4.8.1 (-std=c++11 -Wall -Wextra -pedantic) он не дает никаких предупреждений. Запуск кода выводит следующий результат:

throw
catch

Поэтому конструктор копирования бросает при вызове, но не рассматривается внутри operator =(), и поэтому обещание noexcept выполнено. В противном случае завершение будет вызвано до того, как catch может быть распечатано.