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

C++中用void返回值签名删除拷贝/移动赋值运算符是否合法?

关于用void返回值删除赋值运算符的问题解答

好问题!咱们从编译器表现、标准要求、潜在坑点这几个维度来拆解:

1. 你的写法在GCC/Clang下是可行的

你给出的代码:

struct A { 
    A() = default; 
    void operator = (const A &) = delete; 
    void operator = (A &) = delete; 
    void operator = (A &&) = delete; 
};

确实能被GCC和Clang正常接受,并且达到禁止拷贝/移动赋值的效果——因为你显式删除了这些赋值运算符重载,编译器不会再生成默认的版本。

2. C++标准对删除赋值运算符的签名要求

首先明确:标准并没有强制要求赋值运算符的返回值必须是类的引用,这只是行业惯例(为了支持链式赋值,比如a = b = c)。

根据C++标准的定义:

  • 拷贝赋值运算符是类的非模板成员函数operator=,参数类型为X&、const X&、volatile X&或const volatile X&中的任意一种,返回值类型可以是任意的。
  • 移动赋值运算符是类的非模板成员函数operator=,参数类型为X&&,返回值类型同样无强制要求。

只要你声明了这类签名的operator=(哪怕返回void)并标记为delete,编译器就会认为你已经“用户声明”了赋值运算符,从而不会生成默认的版本。

不过这里有个关键细节:要确保你删除的签名覆盖了所有可能触发默认赋值的场景。比如:

  • 如果你只删除void operator=(A&),编译器依然会生成默认的A& operator=(const A&),此时用const左值赋值依然能通过编译,达不到禁止赋值的目的。
  • 你的代码里删除了const A&、A&、A&&三个版本,其实A&版本是冗余的——因为非常量左值可以隐式转换为const A&,删除const A&就已经覆盖了左值赋值的场景。但多删也不会有问题,只是没必要。

3. 潜在的干扰因素

要注意几个容易踩坑的点:

  • 模板版operator=不抑制默认生成:如果你写的是模板形式的赋值运算符(比如template<typename T> void operator=(const T&) = delete;),它不会阻止编译器生成默认的拷贝/移动赋值运算符,必须删除非模板的版本才行。
  • 基类的赋值运算符:如果你的类继承了某个有赋值运算符的基类,默认情况下基类的赋值运算符不会被继承(C++11及以后需要用using声明才会继承),但如果基类的运算符是public的,可能会通过基类对象的赋值影响派生类,需要额外注意。
  • 链式赋值的编译错误:虽然你已经删除了赋值运算符,但如果有人不小心写了链式赋值(比如a = b = c),因为你的operator=返回void,会直接触发编译错误——不过这本来就是你想要的禁止赋值的效果,所以不算问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:47:36