C++ Template: Why do I get unresolved externals with my template code?
Q: I have the following code:
// In foo.h
template <typename T>
T foo(const T& t);
// end of foo.h
// In foo.cpp
template <typename T>
T foo(const T& t)
// function body
// end of foo.cpp
// In main.cpp
int i = 1;
int j = foo(i);
But when I compile it, the linker tells me that "foo" (in whatever way the complier mangles foo<int>) is an unresolved external symbol.
A: There are actually two solutions to this problem - the "correct" one and the one that works. But before getting to that we need to understand why there is a problem.
The thing to understand about template code is that it's, well, a template. It's not real code. It's like when you have a letter template in your word processor: it's not a real letter until you fill in details like the person it's addressed to. In the example given, the compiler can't compile the definition of foo in "foo.cpp" because it doesn't know what T is. Until it knows that, it can't decide whether or not the operation you perform using T objects are valid or not. For example foo might call a specific member function of T called, say, bar(). But T is only a placeholder. Until you call foo with a specific type, how can the compiler know whether or not the code is valid?
So it comes down to this: templated functions (and classes) don't actually exist until you use them. And in order for the compiler to generate the real code for the function (or class), it has to have the complete definition of the template available at the point of use. If the full definition is not available, the compiler will assume that it's been defined somewhere else and just plant code for the call. And here's the problem with the example above: at the time foo is used (in main()), the compiler only has the declaration of foo available, so it assumes that foo<int> (because that's the instantiation that's wanted) is defined elsewhere. But the definition is in foo.cpp, and there is no use of foo<int> there, so the compiler won't bother generating it. And so we get to the situation that compiling main() plants a call to foo<int>, but compiling foo.cpp produces nothing. And that leads to an unresolved external reference.
The "correct" solution.
I call this correct, because it keeps the example code the way it is laid out. The standard defines a keyword export that is designed for just this situation. By defining foo in foo.cpp with export:
The definition becomes available to the compiler whilst it is comping main.cpp, and so it now generates the code for foo<int> and the external is resolved.
The solution that works.
Unfortunately, not many compilers support export, so the "correct" solution is more likely to produce a compilation error that it is to solve the problem. So the practical solution is define the function in the header file as an inline function:
This way, because it's in the header file, the compiler can see it at the point of use and can generate the appropriate code and the external is resolved.
For those who think that's a bit ugly, and would like a solution that's half-way to the "correct" one (for that day when their compiler supports export), put the inine definition into a file called "foo.inl" and #include "foo.inl" at the end of the original foo.h. Then, when you get your new compiler, rename foo.inl to foo.cpp, change inline to export and remove the #include from foo.h.
Last edited by Andreas Masur; July 24th, 2005 at 05:24 AM.