内联与外联定义的隐式异常规范为何存在差异?
内联与外联定义的隐式异常规范为何存在差异?
这个问题其实是C标准演化过程中留下的一个细节坑,我当初在C11时代踩过一模一样的雷,咱们掰开揉碎了说:
内联默认定义的情况:
当你在类体内直接写X& operator = (X&&) = default;时,不管是哪个C++版本,编译器都会在类定义的上下文里(此时所有成员的类型都已明确),自动推导这个默认移动赋值的异常规范。因为你的X里没有任何会抛出异常的成员操作,所以编译器会给这个函数加上noexcept(true)的属性,std::is_nothrow_move_assignable_v<X>自然能通过静态断言。外联默认定义的情况:
这里的差异根源在C11到C17的标准变化:- 在C++11中,如果你在类内只声明移动赋值(不写
= default;),然后到类外再用= default;定义,编译器不会自动给这个函数推导noexcept属性——哪怕它的操作明明不会抛异常。因为此时标准要求,显式默认的外联特殊成员函数,必须在类内声明时显式指定noexcept,否则默认视为可能抛出异常。所以std::is_nothrow_move_assignable_v<X>会判定为false,静态断言失败。 - 到了C++17,标准修复了这个问题:不管是内联还是外联的显式默认特殊成员函数,编译器都会按照隐式声明的规则自动推导
noexcept属性。也就是说,只要成员操作都不抛异常,外联默认的移动赋值也会被标记为noexcept(true),这时候断言就能通过了。
- 在C++11中,如果你在类内只声明移动赋值(不写
如果要在C++11里让外联默认的移动赋值也满足noexcept,你需要在类内声明的时候就显式加上noexcept:
struct X { X() = default; X(const X&) = default; X(X&&) = default; X& operator = (const X&) = default; X& operator = (X&&) noexcept; // 显式声明noexcept }; X& X::operator=(X&&) = default;
这样编译器就会认可它的noexcept属性,静态断言就能正常通过了。
备注:内容来源于stack exchange,提问作者mentalmushroom
相关产品推荐
相关产品推荐

