std::is_nothrow_move_constructible是否要求noexcept析构函数?代码合规性问询
std::is_nothrow_move_constructible的标准判定与编译器行为解析 你遇到的这个情况其实是旧版本编译器的实现bug,完全不符合C++标准。下面我逐个拆解你的问题:
1. 是否符合C++标准?编译器的行为是错误的
根据C++标准的定义:
std::is_nothrow_move_constructible<T>的判定仅需要两个核心条件:- 类型
T是可移动构造的(即存在可访问的移动构造函数); - 调用移动构造函数的表达式
T(std::declval<T&&>())本身是noexcept的。
- 类型
你的Type类完全满足这两个条件:移动构造函数明确标记为noexcept,构造过程没有任何会抛出异常的因素。而析构函数的noexcept(false)属性和移动构造的noexcept判定完全无关——构造和析构是两个独立的操作,标准从未要求移动构造的异常安全判定要考虑析构函数的异常属性。
你用到的VS2017 (15.5)、gcc 6.4.0和clang 4.0.1都是比较旧的版本,它们的标准库实现错误地将析构函数的noexcept状态纳入了std::is_nothrow_move_constructible的判定逻辑,这才导致静态断言失败。升级到更新版本的编译器(比如gcc 7+、clang 5+、VS2017 15.7+)后,这个问题就会消失。
2. std::is_nothrow_move_constructible是否要求类型具备noexcept析构函数?
答案是完全不需要。
如上面所说,这个特性的判定只关注移动构造函数本身的异常安全性,和析构函数没有任何关系。析构函数的noexcept属性会影响std::is_nothrow_destructible<T>的结果,但不会干扰移动构造相关的特性判定。
3. 移动构造语句中a的析构时机是否与此问题相关?
完全不相关。
执行Type a; Type b(std::move(a));时,std::move(a)只是将a转换为右值引用,用于初始化b——整个过程中不会销毁a,a的析构会在它的作用域结束时(比如代码块末尾)才会发生。而std::is_nothrow_move_constructible的判定只关心b的移动构造过程是否安全,和a的析构没有任何关联。
内容的提问来源于stack exchange,提问作者Simon

