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

显式声明析构函数后,为何仍能生成C++移动操作?

疑问:为什么带用户声明析构函数的类仍被判定为可 noexcept 移动构造?

我查阅到C++标准草案N3337的[class.copy]/9条款:若类X未显式声明移动构造函数,仅当X无用户声明的拷贝构造函数、拷贝赋值运算符、移动赋值运算符、析构函数,且移动构造函数不会被隐式定义为删除时,才会被隐式声明为默认移动构造函数。另外,将析构函数声明为= default也属于用户声明。

但运行如下代码时:

struct A1 { virtual ~A1() {};  };

struct B1 : public A1 {
    ~B1() override {};  };

struct A2 { virtual ~A2() = default;  };

struct B2 : public A2 {
    ~B2() override = default;  };

struct A_NO_VIRTUAL {
    ~A_NO_VIRTUAL() {};  };

struct B_NO_VIRTUAL : public A_NO_VIRTUAL {
    ~B_NO_VIRTUAL() {};  };


int main() {  
    std::cout << std::is_nothrow_move_constructible_v<B1> 
              << std::is_nothrow_move_constructible_v<B2> 
              << std::is_nothrow_move_constructible_v<B_NO_VIRTUAL> 
              << std::endl; 
}

实际输出为111,但我预期是000,这是为什么?


核心原因:移动构造回退到拷贝构造,且拷贝构造是noexcept的

你预期000的逻辑是:因为这些类都有用户声明的析构函数,所以编译器不会隐式生成移动构造函数,因此is_nothrow_move_constructible应该返回false。但这里忽略了C++中移动操作的回退规则:

当类没有可用的移动构造函数时,编译器会尝试使用拷贝构造函数来完成移动操作——因为右值可以绑定到const T&类型的参数。而你的代码里的类满足两个关键条件:

  1. 所有类都没有用户声明的拷贝构造函数,编译器会隐式生成默认拷贝构造函数;
  2. 默认生成的拷贝构造函数是noexcept的:因为类本身是空类,所有基类和成员的拷贝构造都不会抛出异常,符合默认拷贝构造函数的noexcept判定规则。

而std::is_nothrow_move_constructible_v的判定逻辑是:类型是否能通过右值不抛出地完成构造——不管是用专属的移动构造函数,还是用能接受右值的拷贝构造函数,只要满足这个条件就返回true。

所以你的代码里,虽然没有隐式生成移动构造函数,但隐式生成的拷贝构造函数可以不抛出地处理右值,因此这个 trait 会返回1。

如果想要让is_nothrow_move_constructible_v返回false,可以显式声明拷贝构造函数并标记为noexcept(false),或者显式删除移动构造函数,这样就不会回退到拷贝构造逻辑了。


内容的提问来源于stack exchange,提问作者dwto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:22:35