如何在含Q_OBJECT宏的类中创建可变参数方法?
带有Q_OBJECT宏的Qt类与可变参数模板函数的链接问题
本文聚焦于带有Q_OBJECT宏的Qt类中使用可变参数模板函数时出现的链接错误问题,并非泛泛讨论未解析链接符号。Q_OBJECT宏会由moc预编译器自动生成元对象相关代码,但出现的链接错误看似与moc生成的符号有关,实际另有原因。
一、初始问题场景与错误
尝试创建继承QObject(以支持Qt事件)且包含可变参数模板函数的类,以下是最小复现示例:
#include <iostream> #include <string> #include <initializer_list> #include <qobject.h> #include <qdebug.h> class aClass : public QObject { Q_OBJECT public: template <typename T> void func(T t) { qDebug() << t; } template<typename T, typename... Args> void func(T t, Args... args) // 递归可变参数函数 { qDebug() << t; func(args...) ; } }; int main() { QString str1( "Hello" ); aClass a; a.func(1, 2.5, 'a', str1); }
编译运行时出现以下链接错误:
main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __cdecl aClass::metaObject(void)const " (?metaObject@aClass@@UEBAPEBUQMetaObject@@XZ) main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual void * __cdecl aClass::qt_metacast(char const *)" (?qt_metacast@aClass@@UEAAPEAXPEBD@Z) main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual int __cdecl aClass::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@aClass@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z)
移除QObject相关代码或把函数移到类外时,代码可正常运行。
二、代码拆分后的新错误
若将类定义放在头文件、模板函数实现放在主cpp文件中,会出现新的链接错误:
main.h
#ifndef MAIN_H #define MAIN_H #include <qobject.h> #include <qdebug.h> class aClass : public QObject { Q_OBJECT public: template <typename T> void func(T t); template<typename T, typename... Args> void func(T t, Args... args); // 递归可变参数函数 }; #endif // MAIN_H
main.cpp
#include <qdebug.h> #include "main.h" template <typename T> void func(T t) { qDebug() << t; } template<typename T, typename... Args> void func(T t, Args... args) // 递归可变参数函数 { qDebug() << t; func(args...) ; } int main() { QString str1( "Hello" ); aClass a; a.func(1, 2.5, 'a', str1); }
错误信息:
main.obj:-1: error: LNK2019: unresolved external symbol "public: void __cdecl aClass::func<int,double,char,class QString>(int,double,char,class QString)" (??$func@HNDVQString@@@aClass@@QEAAXHNDVQString@@@Z) referenced in function main
三、可行的代码结构
当把模板函数的实现直接放在头文件的类声明中时,代码可正常运行:
main.h
#ifndef MAIN_H #define MAIN_H #include <qobject.h> #include <qdebug.h> class aClass : public QObject { Q_OBJECT public: template <typename T> void func(T t) { qDebug() << t; } template<typename T, typename... Args> void func(T t, Args... args) // 递归可变参数函数 { qDebug() << t; func(args...) ; } }; #endif // MAIN_H
main.cpp
#include <qdebug.h> #include "main.h" int main() { QString str1( "Hello" ); aClass a; a.func(1, 2.5, 'a', str1); }
四、核心需求
需要在同一个类中同时实现Qt事件支持与可变参数函数,具体要求为:前三个参数为固定的不同类型,后续可接0到任意数量的QString参数。项目基于C11,也可接受C17方案。
此问题本质与C++模板函数需在头文件中实现的规则相关,容易被误认为是moc预编译器的问题,尤其是初始错误均涉及元对象相关符号。
内容的提问来源于stack exchange,提问作者CodeLurker
相关产品推荐
相关产品推荐

