multiple definition of template specialization when using different objects

C++Templates

C++ 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:

  1. Make your instantiation inline.

  2. 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 void f(T) { /* ... / } template inline T > g(T) { / ... / } > > template<> inline void f<>(int) { / ... / } // OK: inline > template<> int g<>(int) { / ... */ } // OK: not inline — > end example ]

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpzanoniView Question on Stackoverflow
Solution 1 - C++Stuart GolodetzView Answer on Stackoverflow
Solution 2 - C++David Rodríguez - dribeasView Answer on Stackoverflow
Solution 3 - C++Edward StrangeView Answer on Stackoverflow
Solution 4 - C++FrancisView Answer on Stackoverflow