#ИМЯ?
Я знаю, что вместо написания:
class A {
public:
A(A&&) noexcept = default;
};
Лучше писать
class A {
public:
A(A&&) noexcept;
};
inline A::A(A&&) noexcept = default;
Причины, о которых я слышал:
-
Он избегает конструктора, который deleted
. Компилятор выдаст ошибку, если он не может определить функцию.
-
Конструктор перемещения объявляется noexcept
даже если некоторые из конструкторов перемещения полей элементов не аннотируются с noexcept
.
Может ли кто-нибудь объяснить немного больше об теории различий?
Ответы
Ответ 1
Для описания класса/метода используется только объявление, поэтому при выполнении
class A {
public:
A(A&&) noexcept;
};
Вы даже можете реализовать A::A(A&&)
(определение может быть в разных TU)
Когда вы реализуете его с помощью:
A::A(A&&) noexcept = default;
Компилятор должен сгенерировать метод (он не может определить, если он неявно удален, поскольку существует точный метод объявления) и предоставляет диагностику, если он не может.
Но когда вы объявляете его внутри класса:
class A {
public:
A(A&&) noexcept = default;
};
Это "часть" декларации. поэтому он может быть неявно удален (из-за члена или базового класса).
То же самое относится к noexcept
.
Другим преимуществом для определения определения в выделенном TU является то, что определение требуемых зависимостей может быть только в этом TU, а не в каждом месте, где будет создан метод. (Полезно для идиомы pimpl, например).
Один из недостатков определения разделения и объявления заключается в том, что теперь метод "предоставляется пользователем", который может влиять на характеристики как тривиально-неразрушаемые/скопируемые/...
Ответ 2
Поведение описано в [dcl.fct.def.default] p3, в котором говорится:
Если функция, которая явно дефолтна, объявляется с помощью спецификатора noexcept, который не дает той же спецификации исключения, что и неявное объявление (18.4), тогда
(3.1) - если функция явно дефолтована по первому объявлению, она определяется как удаленная;
(3.2) - в противном случае программа плохо сформирована.
Обратите внимание на изменение формулировок на С++ 20, но намерение для этого случая одинаково. Я считаю, что формулировка С++ 17 проще для поиска.
Например:
struct S {
S( S&& ) noexcept(false) = default;
};
Конструктор перемещения определяется как удаленный, поскольку из-за [except.spec] p7:
Конструктор с неявным объявлением для класса X или конструктор без спецификатора noexcept, который имеет дефолт по его первому объявлению, имеет потенциально-бросающую спецификацию исключений тогда и только тогда, когда любая из следующих конструкций потенциально бросает:
(7.1) - конструктор, выбранный с помощью разрешения перегрузки в неявном определении конструктора для класса X, для инициализации потенциально сконструированного подобъекта или
(7.2) - подвыражение такой инициализации, такое как выражение аргумента по умолчанию, или,
(7.3) - для конструктора по умолчанию - инициализатор элемента по умолчанию.
ни один из случаев не выполняется.
Если мы вернемся к [dcl.fct.def.default] p3, в противном случае программа будет плохо сформирована. Искаженные программы требуют диагностики, поэтому, если мы изменим первый пример следующим образом (см. Его в прямом эфире):
struct S {
S( S&& ) noexcept(false) ;
private:
int i;
};
S::S( S&&) noexcept(false) = default ;
он будет производить диагностику, например:
error: function 'S::S(S&&)' defaulted on its redeclaration with an exception-specification that differs from the implicit exception-specification 'noexcept'
S::S( S&&) noexcept(false) = default ;
^
Обратите внимание на clang-ошибку, связанную с этим случаем, кажется, что они не следуют из отчета о дефектах 1778.
Возможно, вы захотите отметить объявление функции по умолчанию после первого объявления, которое охватывает некоторые проблемы оптимизации/интерфейса.