6

我知道标记为的构造函数将尽可能=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时会出现此问题。

4

2 回答 2

4

我现在意识到我可以做到这一点,直到现在我才想到:

struct Foo
{
    Foo() = default;
    Foo(Foo&&) noexcept;
};
Foo::Foo(Foo&&) noexcept = default; // now it is noexcept

还是第二个问题,为什么是noexcept(false)默认的?适用。

于 2015-04-24T15:06:14.440 回答
2

§8.4.2/2 [dcl.fct.def.default]中涵盖了管理您的两个示例的异常规范的规则

...如果一个函数在其第一个声明中被显式默认,
- 它被隐式认为是隐constexpr式声明,
-它被隐式认为具有与隐式声明相同的异常规范(15.4) , 和
- ...

Bar的移动构造函数是noexcept(true)因为在§15.4/14 [except.spec]

隐式声明的特殊成员函数(第 12 条)应具有异常规范Iff是隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符或移动赋值运算符,当且仅当函数的异常规范直接允许时,其隐式异常规范指定类型 ID 由的隐式定义调用;如果它直接调用的任何函数都允许所有异常,则应允许所有异常,如果它直接调用的每个函数都不允许异常,则不应允许异常TTfff

§8.4.2/2 中的规则不适用于在初始声明后显式默认的特殊成员函数,但析构函数除外,noexcept(true)除非您声明它noexcept(false)或其中之一的析构函数,否则在 §12.4/3 中的析构函数是特殊情况数据成员或基类可以抛出。

因此,除非您指定Foo(Foo&&)noexcept(true),否则假定为noexcept(false)

您需要将noexcept规范添加到声明和以后的显式默认声明的原因可在§15.4中找到

3   两个异常规范兼容如果:
— 两者都是非抛出的(见下文),无论它们的形式如何,
— ...
4   如果函数的任何声明具有不是noexcept 规范的异常规范,允许所有异常,该函数的所有声明,包括定义和任何显式特化,都应具有兼容的异常规范。...

于 2015-04-24T18:09:02.093 回答