This post is not just about unresolved linker symbols in general, but about a variadic function in a class with a Q_OBJECT macro. The macro creates a lot of components automatically generated by the moc precompiler. For reasons not obvious in my code, it is dredging up errors about obscure symbols generated by the moc compiler; when these errors to not occur when component parts of this code are compiled separately! Note that none of the symbols particular to my source occur in the error messages. Questions of this nature are particularly thorny and arcane to debug. (It was wrongly flagged as a dupe of ? about unresolved external symbols in general. It is not a case where I have simply missed defining some procedure in my code, but referenced it.)
I'm trying to create an object that both has Qt events, and has a function that can accept a variable number of arguments. This is a minimal example, although lacking in the events, but with the QObject stuff needed for the events. The problem is that it generates a link error. It's based on the first answer to this SO ?: "Variable number of arguments in C++?"
#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) // recursive variadic function
{
qDebug() << t;
func(args...) ;
}
};
int main()
{
QString str1( "Hello" );
aClass a;
a.func(1, 2.5, 'a', str1);
}
The errors I get are:
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)
If I remove the QObject stuff, it works. If the functions are outside the class, it works. Is there a way to have both in the same class? I want to have three arguments of predetermined types as the first arguments, not of the same types, and then from zero to any number of QString arguments after that. This is for a project in C++11, but a C++17 answer would be acceptable, too.
If I have the class definition in the header, but the function bodies in the main file, it gives a link error:
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); // recursive variadic function
};
#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) // recursive variadic function
{
qDebug() << t;
func(args...) ;
}
int main()
{
QString str1( "Hello" );
aClass a;
a.func(1, 2.5, 'a', str1);
}
I get the link error:
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
It complains about a missing aClass::func<int,double,char,class QString>(int,double,char,class QString)
function.
If I put the function bodies in the class declaration in the header, it works:
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) // recursive variadic function
{
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);
}
This is evidently related to "Why can templates only be implemented in the header file?"; although one would be misled into thinking it was some weirdness due to the moc precompiler - especially since all three of the original errors concerned metaobjects or a metacast.
#include
the file generated by moc at the end of your source file. Please show your exact compilation and link commands, including the invocation of moc. – Postwar