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

vso*_*tco 6 c++ default-constructor noexcept c++11

我知道标记为的构造函数将尽可能=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;
}
Run Code Online (Sandbox Code Playgroud)

如何=default在类外部定义这样的构造函数并制作它noexcept?为什么这样的构造函数noexcept(false)在类外定义?通过智能指针实现PIMPL时会出现此问题.

Pra*_*ian 2

\xc2\xa78.4.2/2 [dcl.fct.def.default]中涵盖了管理两个示例的异常规范的规则

\n\n
\n

...如果一个函数在其第一个声明中显式默认,
\n -constexpr如果隐式声明是,则它被隐式地认为是,
\n -它被隐式地认为具有相同的异常规范,就像它已隐式声明一样声明(15.4),并且
\n — ...

\n
\n\n

Bar的移动构造函数是noexcept(true)因为在\xc2\xa715.4/14 [ except.spec]

\n\n
\n

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

\n
\n\n

\xc2\xa78.4.2/2 中的规则不适用于在初始声明后显式默认的特殊成员函数,析构函数除外,析构函数在 \xc2\xa712.4/3 中是特殊情况,除非您声明noexcept(true)noexcept(false)或者数据成员或基类之一的析构函数可以抛出异常。

\n\n

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

\n\n

noexcept您需要将规范添加到声明和后来的显式默认声明中的原因可以在\xc2\xa715.4中找到

\n\n
\n

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

\n
\n

  • 特别是,如果它在初始声明后被默认,则默认可能发生在不同的 TU 中,因此在仅包含类定义的 TU 中,编译器无法知道该函数将被默认。 (3认同)