Search code examples
c++visual-studio-2010templatesunresolved-external

Unresolved NON template method being called by template method which is correctly instantiated


I searched the whole internet and stack overflow before deciding to ask this question, so I beg your pardon if I overlooked something quite simple, but here it goes.

Working with Visual Studio 2010, I've got a class like this:

class MyClass {
public:
    /* Constructors and other methods omitted */

    template <typename T1>
    CString operator()(const T1 &a1) { return _fmt("dummy", a1); }

    template <typename T1, typename T2>
    CString operator()(const T1 &a1, const T2 &a2) { return _fmt("dummy", a1, a2); }

    template <typename T1, typename T2, typename T3>
    CString operator()(const T1 &a1, const T2 &a2, const T3 &a3) { return _fmt("dummy", a1, a2, a3); }

    template <typename T1, typename T2, typename T3, typename T4>
    CString operator()(const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4) { return _fmt("dummy", a1, a2, a3, a4); }

private:    
    CString _fmt(const char *dummy, ...);
};

The idea is to provide MyClass with a operator () overload that acts like a sprintf() formatter, except the format string is contained in the MyClass instance itself. I would use variadic templates, alas Visual Studio 2010 doesn't support them, hence the half-baked solution is to provide many overload template versions of operator () which in turn all invoke a private _fmt() function which uses va_list/va_start()/va_end() and therefore needs by force an useless 1st parameter (hence the name "dummy").

Compilation goes just fine, however linking gives errors like the following one:

error LNK2019: unresolved external symbol "private: class ATL::CStringT<char,class ATL::StrTraitATL<char,class ATL::ChTraitsCRT<char> > > __cdecl MyClass::_fmt(char const *,...)" (?_fmt@MyClass@@AAA?AV?$CStringT@DV?$StrTraitATL@DV?$ChTraitsCRT@D@ATL@@@ATL@@@ATL@@PBDZZ) referenced in function "public: class ATL::CStringT<char,class ATL::StrTraitATL<char,class ATL::ChTraitsCRT<char> > > __thiscall MyClass::operator()<char [261]>(char const (&)[261])" (??$?R$$BY0BAF@D@MyClass@@QAE?AV?$CStringT@DV?$StrTraitATL@DV?$ChTraitsCRT@D@ATL@@@ATL@@@ATL@@AAY0BAF@$$CBD@Z)

What I understand from the above message is that the templated operator was correctly instantiated, and it contains a call to the _fmt() method which, however, is nowhere to be found by the linker.

But _fmt() is included in a .cpp file which is being compiled and liked in, in fact the constructor is defined there too and the linker doesn't complain about it.

The only way I found to work this around was to put the _fmt() code in the class definition body, in the header file, which is rather ugly.

Any ideas?


Solution

  • Ok, this was a silly one, but since others might stumble over it, in a way or another, I'm answering my own question.

    I was mistakely including the header file <afxmt.h>, which is part of MFC, in order to get the CCriticalSection class definition, whilst the rest of the project was using ATL, and this brought in a different version of CString. Somehow, though, this only affected the MyClass implementation file, whilst the others, even though they were using MyClass, got a different version of CString.

    I solved it by not including <afxmt.h> and implemeting a CCriticalSection class myself.