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

MSVC C++23下如何让编译器对不可移动类使用拷贝而非移动

问题:创建仅允许拷贝、禁用移动的C++类,解决拷贝初始化右值的编译错误

我正在使用MSVC编译器与C++23标准,需要创建一个仅允许拷贝、不允许移动的类。现有代码如下:

#include <cstring>

struct MyClass
{
    MyClass() = default;

    //copy
    MyClass(const MyClass& other) { data = other.data; }
    MyClass& operator=(const MyClass& other) { data = other.data; return *this; }

    //cannot allow move for this class
    MyClass(MyClass&& other) = delete;
    MyClass& operator=(MyClass&& other) = delete;

    int data = 0;
};

int main()
{
    MyClass m = MyClass{}; //tries to use move

    //works
    MyClass newm{};
    m = newm;
}

当执行MyClass m = MyClass{};时,编译器尝试调用已删除的移动构造函数,触发编译错误,报错信息如下:

error C2280: 'MyClass::MyClass(MyClass &&)': attempting to reference a deleted function
message : see declaration of 'MyClass::MyClass'
message : 'MyClass::MyClass(MyClass &&)': function was explicitly deleted

需求:能否修改类定义,让编译器在移动构造不可用时自动使用拷贝构造,使MyClass m = MyClass{};正常运行?


解决方案

方案一:移除显式删除的移动操作(推荐)

不需要显式删除移动构造/赋值运算符,只需保留显式定义的拷贝操作,编译器会自动禁用移动操作,同时在右值初始化场景下回退到拷贝构造。

修改后的代码:

#include <cstring>

struct MyClass
{
    MyClass() = default;

    // 显式定义拷贝构造与拷贝赋值
    MyClass(const MyClass& other) { data = other.data; }
    MyClass& operator=(const MyClass& other) { data = other.data; return *this; }

    // 移除移动操作的删除声明,编译器不会自动生成它们
    int data = 0;
};

int main()
{
    MyClass m = MyClass{}; // 调用拷贝构造,正常编译
    MyClass newm{};
    m = newm; // 正常调用拷贝赋值
}

原理说明

  • C++标准规定:当类显式声明了拷贝构造/赋值时,编译器不会自动生成移动构造/赋值运算符,相当于移动操作被隐式禁用。
  • 对于MyClass m = MyClass{};这种拷贝初始化场景,由于const MyClass&可以绑定到右值,当移动构造不存在时,编译器会直接匹配拷贝构造函数执行。
  • 之前显式删除移动构造的问题在于:删除操作会让该函数“存在但不可用”,编译器会优先尝试匹配右值对应的MyClass&&参数,发现函数被删除后直接报错,不会回退到拷贝构造。

方案二:保留删除声明的兼容写法(可选)

如果需要明确对外表明禁用移动(保留删除声明),可以通过重载接受右值的拷贝构造/赋值函数,覆盖被删除的移动操作匹配逻辑:

#include <cstring>

struct MyClass
{
    MyClass() = default;

    // 常规左值拷贝构造
    MyClass(const MyClass& other) { data = other.data; }
    // 新增:接受右值的拷贝构造,优先匹配右值初始化场景
    MyClass(MyClass&& other) : MyClass(static_cast<const MyClass&>(other)) {}

    // 常规左值拷贝赋值
    MyClass& operator=(const MyClass& other) { data = other.data; return *this; }
    // 新增:接受右值的拷贝赋值
    MyClass& operator=(MyClass&& other) { return *this = static_cast<const MyClass&>(other); }

    // 可保留删除声明,也可以不写(上面的重载已覆盖右值场景)
    // MyClass(MyClass&& other) = delete;
    // MyClass& operator=(MyClass&& other) = delete;

    int data = 0;
};

这种写法通过重载MyClass&&参数的构造/赋值函数,内部调用拷贝逻辑,让右值初始化时优先匹配该重载,避免触发被删除的移动操作报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 05:08:26