如何在类noexcept之外定义默认构造函数?

时间:2015-04-24 15:02:33

标签: c++ c++11 default-constructor noexcept

我知道标记为=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时会出现此问题。

2 个答案:

答案 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-id T当且仅当函数的 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 允许所有异常,所有声明,包括定义并且该函数的任何显式特化都应具有兼容的异常规范。 ...