Qt6+VS2019下自定义operator new类作信号槽参数编译报错
问题描述
定义包含自定义operator new、operator delete的基类BaseClass,MyClass公开继承自BaseClass,复现代码如下:
class BaseClass { void* operator new(size_t size); void operator delete(void* p); }; class MyClass : public BaseClass { public: MyClass(int); };
将MyClass作为Qt信号与槽的传递参数时,Qt5环境下可正常编译运行,但在Visual Studio 2019环境使用Qt6时,会在QMetaType内部代码处触发编译错误,报错原因为无法匹配到MyClass的类专属placement new。
VS2019下的具体编译报错信息如下(已隐去特定路径与类名):
...QtCore\qmetatype.h(2313): error C2660: 'BaseClass::operator new': function does not take 3 arguments : note: see declaration of 'BaseClass::operator new' ...QtCore\qmetatype.h(2310): note: while compiling class template member function 'QtPrivate::QMetaTypeInterface::CopyCtrFn QtPrivate::QMetaTypeForType<T>::getCopyCtr(void)' with [ T=Ty ] ...QtCore\qmetatype.h(2370): note: see reference to class template instantiation 'QtPrivate::QMetaTypeForType<T>' being compiled with [ T=Ty ] ...QtCore\qmetatype.h(2494): note: see reference to class template instantiation 'QtPrivate::QMetaTypeInterfaceWrapper<Ty>' being compiled ...QtCore\qmetatype.h(2537): note: see reference to function template instantiation 'const QtPrivate::QMetaTypeInterface *QtPrivate::qTryMetaTypeInterfaceForType<Unique,QtPrivate::TypeAndForceComplete<MyClass &,std::false_type>>(void)' being compiled with [ Unique=qt_meta_stringdata_..._t ] moc_xxx.cpp(286): note: see reference to variable template 'const QtPrivate::QMetaTypeInterface *const qt_incomplete_metaTypeArray<qt_meta_stringdata_ModelBrowser_t,QtPrivate::TypeAndForceComplete<ModelBrowser,std::integral_constant<bool,1> >,QtPrivate::TypeAndForceComplete<void,std::integral_constant<bool,0> >,QtPrivate::TypeAndForceComplete<void,std::integral_constant<bool,0> >,QtPrivate::TypeAndForceComplete<MyClass &,std::integral_constant<bool,0> >,QtPrivate::TypeAndForceComplete<void,std::integral_constant<bool,0> >,QtPrivate::TypeAndForceComplete<MyClass &,std::integral_constant<bool,0> >,QtPrivate::TypeAndForceComplete<void,std::integral_constant<bool,0> >,...[58]' being compiled
此前尝试为类添加通用类专属placement new(即void* operator new(size_t size, std::align_val_t, void*)),该写法虽然可在VS2019下通过编译,但不符合C++标准,在其他编译器下会编译失败。
根因分析
- 触发编译错误的核心是C++的类域名称查找规则:当类内部声明了同名的
operator new成员函数时,类域名称查找会优先匹配类内成员,不会自动搜索全局作用域下的其他operator new重载,除非显式将全局重载引入类作用域。 - Qt6重构了元类型系统实现,在QMetaType的拷贝构造、对象构造逻辑中,会调用标准全局placement new(签名为
void* operator new(std::size_t, void* ptr) noexcept,定义在<new>头文件中)在预分配内存上构造对象。由于基类自定义的单参数operator new隐藏了全局的placement new重载,重载决议时只能匹配到基类的单参数版本,参数数量不匹配就会触发编译错误。 - Qt5的QMetaType实现未在元类型注册逻辑中通过类域查找调用placement new,因此不会触发该问题。
- 手动添加带
std::align_val_t参数的placement new属于编译器特定扩展,不符合C++标准对标准placement new的签名要求,跨编译器编译会出现签名不匹配问题,不具备可移植性。
符合C++标准的解决方案
优先选择通过using声明引入全局标准operator new重载的方案,完全符合C标准,无额外副作用,所有支持C11及以上版本的编译器均可正常编译:
- 方案1(推荐,改动影响最小):在基类中引入全局
operator new的所有重载,所有派生类自动生效。注意需要先引入<new>头文件确保全局placement new声明可见:
#include <new> class BaseClass { public: void* operator new(size_t size); void operator delete(void* p); // 将全局作用域所有operator new重载引入类域,参与重载决议 using ::operator new; };
- 方案2:如果无法修改基类,可直接在派生类
MyClass中添加同样的using声明,仅对当前派生类生效:
#include <new> class MyClass : public BaseClass { public: MyClass(int); // 引入全局operator new重载 using ::operator new; };
- 方案3:如果自定义的类专属
operator new不需要对派生类生效,可将基类的operator new声明为private,或在派生类中重写默认operator new逻辑,但该方案改动影响范围更大,仅在明确业务逻辑需要时使用。
内容的提问来源于stack exchange,提问作者Hans Olsson
相关产品推荐
相关产品推荐

