考虑这个课程:
class C1 {
C1(C1&&) = default; // declare and define move ctor
};
因为C1的移动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移动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的推理是否正确?
答案 0 :(得分: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>{}, "");
}
答案 1 :(得分:1)
解释是正确的,是的。第一个声明后的默认值意味着生成函数体而不对声明执行任何魔术。如果定义在不同的翻译单元中,您可以在
之间自由切换C2::C2(C2&& rhs) {/* hand-written implementation here */}
和
C2::C2(C2&& rhs) = default;
并且该类的用户看不到。既然你没有默认 在第一个声明中,声明实际上是noexcept(false)和(无论好坏)保持这种方式,无论你的类中的子对象如何。违约的第一个宣言就是进一步“魔术” 也可以生成声明的noexcept规范。