我知道标记为=default
的构造函数将尽可能“尝试”为noexcept
。但是,如果我在课程中定义 ,则不再是noexcept
,正如您可以从此代码中看到的那样:
#include <iostream>
#include <utility>
#include <type_traits>
struct Bar
{
Bar() = default;
Bar(Bar&&) = default; // noexcept
};
struct Foo
{
Foo() = default;
Foo(Foo&&);
};
// moving the definition outside makes it noexcept(false)
Foo::Foo(Foo&&) = default; // not noexcept anymore
int main()
{
Foo foo;
Bar bar;
std::cout << std::boolalpha;
// checks
std::cout << std::is_nothrow_move_constructible<Bar>::value << std::endl;
std::cout << std::is_nothrow_move_constructible<Foo>::value << std::endl;
}
如何在类外部定义这样的=default
构造函数并使其成为noexcept
?为什么这样的构造函数noexcept(false)
如果在类之外定义?通过智能指针实现PIMPL时会出现此问题。
答案 0 :(得分:4)
我现在意识到我可以做到这一点,直到现在它并没有让我想到:
struct Foo
{
Foo() = default;
Foo(Foo&&) noexcept;
};
Foo::Foo(Foo&&) noexcept = default; // now it is noexcept
还是第二个问题为什么默认为noexcept(false)
?适用。
答案 1 :(得分:2)
§8.4.2/ 2 [dcl.fct.def.default]
中介绍了管理两个示例的异常规范的规则...如果某个函数在其第一个声明中明确默认,则为 - 如果隐含声明是,则隐含地认为它是
constexpr
- 隐含地认为它具有相同的异常 - 规范,就像它已被隐式声明一样(15.4),并且 - ......
Bar
的移动构造函数为noexcept(true)
,因为在§15.4/ 14 [except.spec]
隐式声明的特殊成员函数(第12条)应具有异常规范。 如果
f
是隐式声明的默认构造函数,复制构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常-specification 指定 type-idT
当且仅当函数的 exception-specification 允许T
时由f
的隐含定义调用;如果它直接调用的任何函数允许所有异常,f
将允许所有异常,并且f
将不允许任何异常 如果它直接调用的每个函数都不允许例外。
§8.4.2/ 2中的规则不适用于在初始声明后已明确默认的特殊成员函数,除了析构函数,在§12.4/ 3中特殊的除外noexcept(true)
除非你声明它noexcept(false)
或其中一个数据成员或基类的析构函数可以抛出。
因此,除非您将Foo(Foo&&)
指定为noexcept(true)
,否则会假定为noexcept(false)
。
您需要在声明和后来的显式默认声明中添加noexcept
规范的原因可在§15.4
3 如果出现以下情况,异常规范 - 两者都是非投掷(见下文),无论其形式如何,
- ......
4 如果函数的任何声明都有异常 - 规范,而不是 noexcept-specification 允许所有异常,所有声明,包括定义并且该函数的任何显式特化都应具有兼容的异常规范。 ...