Question

I am trying to make an example of using templates in order to learn how to use them. I have made a project in Visual Studio 2010 C++ with three files:

Controller.cpp:

#include <iostream>

#include "Foo.h"

using namespace std;

int main(){

    Example<int,string> example;

    example.appols(5, "Hi");

    return 0;
}

Foo.h:

#include <iostream> 

using namespace std;

template <class T, class E>
class Foo{
public:
    void printThis(T t, E e);
};

template <class T, class E>
class Example{
public:
    void appols(T t, E e);
};

Foo.ipp:

#include <iostream>

#include "Foo.h"

using namespace std;

template<class T, class E>
void Foo<T, E>::printThis(T t, E e){
    cout << "FOO! " << t << ' ' << e << endl;
}

template<class T, class E>
void Example<T, E>::appols(T t, E e){
    cout << "APPOLS!! " << t << ' ' << e << endl;
    Foo<T,E> f;
    f.printThis(t,e);
}

When I try to compile, I get:

1>------ Build started: Project: Template Practive, Configuration: Debug Win32 ------
1>Controller.obj : error LNK2019: unresolved external symbol "public: void __thiscall Example<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::appols(int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?appols@?$Example@HV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@QAEXHV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function _main
1>C:\Users\Matthew\documents\visual studio 2010\Projects\Template Practive\Debug\Template Practive.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Could you help me see what I have done wrong here and where error LNK2019 is coming from? Thank you in advance!

Was it helpful?

Solution

Deciphering the Error Message

Let's try to decode the error message first:

1>Controller.obj : error LNK2019: unresolved external symbol 
"public: void __thiscall Example<int,class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> > >::appols(int,class
std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)"
(?appols@?$Example@HV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@QAEXHV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) 
referenced in function _main

We can start to reduce to noise/signal ratio in this error message.

The part with (?appols@?$Example@HV?$basic_string@DU?$char_traits@D@std@@... is just C++ name mangling. It's intended for the C++ compiler, and it's not something meant to be human-readable. So we can just discard it.

The simplified error message becomes:

1>Controller.obj : error LNK2019: unresolved external symbol 
"public: void __thiscall Example<int,class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> > >::appols(int,class
std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)"
referenced in function _main

The first line tells us that there is an "unresolved external symbol". Let's try to identify it, looking at the next lines.

The parts:

class std::basic_string<char,struct std::char_traits<char>...

are just the long name of std::string (which corresponds to std::basic_string template with char as the character type and using the default char traits and the default allocator).

So, we can further reduce the noise, substituting std::string instead of those verbose parts:

1>Controller.obj : error LNK2019: unresolved external symbol 
"public: void __thiscall Example<int,string>::appols(int,string)"
referenced in function _main

Now the error is much clearer.

The __thiscall part is just a calling convention used by default on C++ member functions.

So, the linker is complaining about this method:

"public: void Example<int,string>::appols(int,string)"

which corresponds to your Example<T,E>::appols():

template <class T, class E>
class Example{
public:
    void appols(T t, E e);
};

when T=int and E=std::string.

This corresponds to what you have in your main() function in Controller.cpp:

Example<int,string> example;

example.appols(5, "Hi");

Diagnosis and Solution to the Problem

The problem is that all the template source code must be in header files, not in .cpp files.

In fact, the compiler needs the definition of the template code in order to instantiate it.

So, you can move the code from your Foo.ipp file to Foo.h (or just #include "Foo.ipp" from your Foo.h), and add inline to the template function definitions:

// New code in Foo.h

template<class T, class E>
inline void Foo<T, E>::printThis(T t, E e){
    cout << "FOO! " << t << ' ' << e << endl;
}

template<class T, class E>
inline void Example<T, E>::appols(T t, E e){
    cout << "APPOLS!! " << t << ' ' << e << endl;
    Foo<T,E> f;
    f.printThis(t,e);
}

Note also that using namespace std; is really bad in header files, since this will pollute the global namespace for all the clients that include your headers. Just use the std:: prefix explicitly in header files (e.g.: std::cout, std::endl, etc.).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top