Qt QList存储含引用成员结构体时报operator=被删除错误
问题成因
- C++ 标准的硬性规则:如果结构体/类包含非静态引用成员,编译器会直接删除默认生成的拷贝赋值运算符
operator=。原因很简单:引用在初始化完成后就永远绑定到固定对象,不存在“重新绑定到另一个对象”的语法支持,逐成员赋值的默认逻辑对引用成员完全不成立,编译器没法生成符合语义的赋值实现。 - 你遇到报错和“取出结构体引用成员”的操作没有直接关系,触发点是
QList的内部实现要求:QList 作为值语义容器,在插入元素、扩容迁移元素的过程中,必须调用元素类型的拷贝赋值运算符完成元素搬移。你的Material和测试用的Kram类型都没有可用的拷贝赋值运算符,只要往QList里存这类对象,编译阶段就会报错,循环取成员的代码只是刚好触发了模板实例化的报错路径而已。 - 你之前尝试自定义实现
operator=走不通是必然的:你没法在赋值运算符里修改左值对象引用成员的绑定目标,就算硬写逻辑把右值对象的内容拷贝到左值引用指向的内存,也完全不符合赋值运算符的语义,会引发隐蔽的逻辑错误。你以为写了自定义构造函数就满足要求,其实构造函数只解决对象初始化的问题,解决不了容器需要的元素可拷贝/可赋值的要求。
修复方案
选以下任意一种符合你业务逻辑的方案即可:
- 最通用方案:将引用成员替换为指针成员。指针本身是可拷贝、可赋值的,完全满足QList对元素类型的值语义要求,只要你保证指针指向的对象生命周期长于容器中存储的结构体实例生命周期即可。
以测试用的Kram结构体为例,修改后代码如下:
取用时正常解引用即可:struct Kram { QString* text; Kram(QString& s): text(&s) {} };
你的Material结构体同理,把QString& anothertext = *liste[i].text;Item& item改成Item* item,构造时传入对象地址即可。 - 标准库封装方案:用
std::reference_wrapper替代原生引用。这个标准库类型对引用做了可拷贝、可赋值的封装,不会改变引用语义,不需要修改原有对象访问逻辑太多,使用时调用get()方法就能拿到原引用。
示例代码:#include <functional> struct Kram { std::reference_wrapper<QString> text; Kram(QString& s): text(s) {} }; // 取用时 QString& anothertext = liste[i].text.get();
注意:不要尝试用投机取巧的方式绕过类型限制硬写赋值运算符,引用的绑定特性是语言层面的,强行实现的赋值运算符会出现“修改了其他对象的值”的反直觉bug,极难排查。
内容的提问来源于stack exchange,提问作者Rolf Hausen
相关产品推荐
相关产品推荐

