You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

内联与外联定义的隐式异常规范为何存在差异?

内联与外联定义的隐式异常规范为何存在差异?

这个问题其实是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里让外联默认的移动赋值也满足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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 11:13:00