multiple definition of template specialization when using different objects
C++TemplatesC++ Problem Overview
When I use a specialized template in different object files, I get a "multiple definition" error when linking. The only solution I found involves using the "inline" function, but it just seems like some workaround. How do I solve that without using the "inline" keyword? If that's not possible, why?
Here is the example code:
paulo@aeris:~/teste/cpp/redef$ cat hello.h
#ifndef TEMPLATE_H
#define TEMPLATE_H
#include <iostream>
template <class T>
class Hello
{
public:
void print_hello(T var);
};
template <class T>
void Hello<T>::print_hello(T var)
{
std::cout << "Hello generic function " << var << "\n";
}
template <> //inline
void Hello<int>::print_hello(int var)
{
std::cout << "Hello specialized function " << var << "\n";
}
#endif
paulo@aeris:~/teste/cpp/redef$ cat other.h
#include <iostream>
void other_func();
paulo@aeris:~/teste/cpp/redef$ cat other.c
#include "other.h"
#include "hello.h"
void other_func()
{
Hello<char> hc;
Hello<int> hi;
hc.print_hello('a');
hi.print_hello(1);
}
paulo@aeris:~/teste/cpp/redef$ cat main.c
#include "hello.h"
#include "other.h"
int main()
{
Hello<char> hc;
Hello<int> hi;
hc.print_hello('a');
hi.print_hello(1);
other_func();
return 0;
}
paulo@aeris:~/teste/cpp/redef$ cat Makefile
all:
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
Finally:
paulo@aeris:~/teste/cpp/redef$ make
g++ -c other.c -o other.o -Wall -Wextra
g++ main.c other.o -o main -Wall -Wextra
other.o: In function `Hello<int>::print_hello(int)':
other.c:(.text+0x0): multiple definition of `Hello<int>::print_hello(int)'
/tmp/cc0dZS9l.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: ** [all] Erro 1
If I uncomment the "inline" inside hello.h, the code will compile and run, but that just seems like some kind of "workaround" to me: what if the specialized function is big and used many times? Will I get a big binary? Is there any other way to do this? If yes, how? If not, why?
I tried to look for answers, but all I got was "use inline" without any further explanation.
Thanks
C++ Solutions
Solution 1 - C++
Intuitively, when you fully specialize something, it doesn't depend on a template parameter any more -- so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule as David says. Note that when you partially specialize templates, the partial specializations do still depend on one or more template parameters, so they still go in a .h file.
Solution 2 - C++
The keyword inline
is more about telling the compiler that the symbol will be present in more than one object file without violating the One Definition Rule than about actual inlining, which the compiler can decide to do or not to do.
The problem you are seeing is that without the inline, the function will be compiled in all translation units that include the header, violating the ODR. Adding inline
there is the right way to go. Otherwise, you can forward declare the specialization and provide it in a single translation unit, as you would do with any other function.
Solution 3 - C++
You've explicitly instantiated a template in your header (void Hello<T>::print_hello(T var)
). This will create multiple definitions. You can solve it in two ways:
-
Make your instantiation inline.
-
Declare the instantiation in a header and then implement it in a cpp.
Solution 4 - C++
Here is some piece of C++11 standard related to this issue:
> An explicit specialization of a function template is inline only if it
> is declared with the inline specifier or defined as deleted, and
> independently of whether its function template is inline. [ Example:
>
> template
So if you make some explicit(aka full) specializations of templates in *.h
file, then you will still need inline
to help you get rid of the violation of ODR.