I'm not familiar with c++ templates so my first question was where should I put this template's implementation, in the .cpp or .h file of the processor?
Put it in the header file. That's the simplest and most robust solution. Typically, you want to put definitions of functions (i.e. their function body) in source files (.cpp), as source files can be compiled independently. But this isn't possible for templates(*).
(*) slightly simplified.
Class templates are just blueprints for classes, function templates are blueprints for functions. That is, function templates are not functions, in other words "template function" is misleading, it's not a function but a template/blueprint.
The process of building a function from a function template (or class from a class template) is called instantiation. The result is an instantiated function, or, more generally, a function template specialization.
Template specializations are no templates. A function template specialization is just an ordinary function with a weird name; a class template specialization is just a class with a weird name.
A template will only be instantiated for some specific sets of template arguments:
- Either if you explicitly ask it to be instantiated
- or if you just use a specialization (-> implicit instantiation).
The second way is by far the more common. An example:
template<class T>
struct my_type
{
T mem;
};
// using the specialization -> implicit instantiation
my_type<int> o;
my_type<double> p;
This will instantiate the class template my_type
once for the template argument int
, and once for the template argument double
. This creates two independent and unrelated types with similar names: my_type<int>
and my_type<double>
And similarly for functions; except that for functions, you typically do not explicitly provide the template arguments. Instead, you let the compiler deduce the template arguments from the types of the function arguments. Example:
template<class T>
void foo(T param)
{
std::cout << param;
}
foo<int>(42); // explicitly specifying the template arguments -- DON'T DO THAT
foo(21); // template argument *deduction*
The second call will automatically deduce the template argument to be int
. Again, we have created (implicitly instantiated) two functions with similar names: foo<int>(int)
(the name is foo<int>
and it has a single function parameter of type int
) and foo<double>(double)
Why it's bad to put the definitions of templates in a source file: See Why can templates only be implemented in the header file?
Short version: As templates are blueprints, in order to use them, the compiler has to instantiate them. But it can only instantiate what it knows.
If you declare a function template foo
in a header file templ.h
, define it in templ.cpp
and use it in main.cpp
, then:
In
main.cpp
, the compiler doesn't know about the definition of the function template. It can only instantiate the declaration offoo
, but not the definition.In
templ.cpp
, the compiler does know about the definition, and can instantiate it. However, it doesn't know about the uses outsidetempl.cpp
-- therefore it cannot instantiate it for all sets of arguments used outside.
In the example in the OP, that works, but it seems to be an oversight:
[templ.h]
template<class T>
void foo(T);
void ordinary_function();
[templ.cpp]
#include "templ.h"
template<class T>
void foo(T p)
{
std::cout << p;
}
void ordinary_function()
{
foo(42); // implicit instantiation of foo<int>
foo(2.5); // implicit instantiation of foo<double>
}
[main.cpp]
#include "templ.h"
int main()
{
foo(23); // works fine, uses the foo<int> defined in templ.cpp
foo('a'); // linker error: foo<char> not defined
return 0;
}
Because the definition of foo<char>
hasn't been instantiated in templ.cpp
, and it cannot be instantiated in main.cpp
, this produces a linker error.
That's why you should NOT rely on this behaviour. You can use explicit instantiation if you, for some reason, do not want to define the function template in the header file. At least, the explicit instantiation is explicit, and should be documented, so that no surprises happen.
The problem the compiler is actually complaining about has nothing to do with templates ;) It's just a name lookup issue. A simplified example:
#include <iostream>
struct Foo
{
typedef int Bar;
void do_something();
};
Bar Foo::do_something()
{
std::cout << "something\n";
}
When the compiler sees the line Bar Foo::do_something()
it sees Bar
, and cannot find what that name refers to. Hence the error. On the other hand:
#include <iostream>
struct Foo
{
typedef int Bar;
void do_something();
};
Foo::Bar Foo::do_something()
{
std::cout << "something\n";
}
Now you told the compiler where to look for the name Bar
, namely, inside Foo
.