2

考虑这个类:

class C1 {
  C1(C1&&) = default;        // declare and define move ctor
};

因为 C1 的 move ctor 在它的第一个声明中被显式默认,标准的 8.4.2 告诉我们它具有相同的异常规范 (ES),就好像函数被隐式声明一样。然后我们可以使用 15.4/14 和 12.8/15 得出其 ES 为 的结论noexcept(true)

现在考虑一个相同的类 C2,除了它的移动 ctor 在类定义之外是默认的:

class C2 {
  C2(C2&&);                 // declare move ctor
}; 

C2::C2(C2&&) = default;     // define move ctor

C2 的 move ctor 的 ES 是什么?因为它在第一次声明时没有默认,所以 8.4.2/2 不适用。因为它没有明确的 ES,所以 8.4.2/3 不适用。因为它没有隐式声明,所以 15.4/14 不适用。据我所知,这意味着 15.4/12 适用,它表示默认函数 ES 是noexcept(false).

如果我是对的,那意味着 C1 中的移动 ctor 是noexcept(true),但 C2 中概念上相同的移动 ctor 是noexcept(false)

我对 C2 的推理是否正确?

4

2 回答 2

6

是的,你的解释是正确的,如果你公开你的声明,很容易表明 clang 和 gcc 都同意你的推理:

#include <type_traits>

class C1
{
public:
  C1(C1&&) = default;        // declare and define move ctor
};

class C2 {
public:
  C2(C2&&);                 // declare move ctor
}; 

C2::C2(C2&&) = default;     // define move ctor

int
main()
{
    static_assert(std::is_nothrow_move_constructible<C1>{}, "");
    static_assert(!std::is_nothrow_move_constructible<C2>{}, "");
}
于 2015-04-16T23:55:35.273 回答
1

The interpretation is correct, yes. Defaulting after the first declaration means generating the function body without performing any magic on the declaration. If the definition is in a different translation unit, you can freely switch between

C2::C2(C2&& rhs) {/* hand-written implementation here */}

and

C2::C2(C2&& rhs) = default;

and that is not visible to the users of the class. Since you didn't default on the first declaration, the declaration is effectively noexcept(false) and (for better or worse) stays that way regardless of the subobjects in your class. Defaulting on the first declaration kicks in further "magic" in that the noexcept-specification of your declaration can also be generated.

于 2015-04-17T07:41:26.527 回答