120

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

FluxLemur
  • 1,827
  • 16
  • 18
pzanoni
  • 2,979
  • 2
  • 18
  • 18

4 Answers4

160

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.

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
  • Hmmm I am still a bit confused about how it breaks the ODR. Because you only define the fully specialized template once. You may be creating the object multiple times in different object files (ie. in this case it is instantiated in other.c and main.c) but the original object itself is defined only in one file - in this case `hello.h`. – jlcv Apr 08 '15 at 17:02
  • 5
    @JustinLiang: The header is included in two separate .c files - that has the same effect as if you'd written its contents (including the full specialization) directly into the files in which it's included at the relevant places. The One Definition Rule (see http://en.wikipedia.org/wiki/One_Definition_Rule) says (among other things): "In the entire program, an object or non-inline function cannot have more than one definition". In this case, the full specialization of the function template is in essence just like a normal function, so unless it's inline it can't have more than one definition. – Stuart Golodetz Apr 09 '15 at 22:25
  • Hmmm, I noticed that when we don't have a templated specialization this error will not appear. Let's say we had two different functions that were defined in the header file, outside of the class, they will still work without the inline? For example: http://pastebin.com/raw.php?i=bRaiNC7M. I took that class and included it in two files. Wouldn't this have" the same effect as if you'd written the contents" directly into the two files and thus there will be a multiple definition error? – jlcv Apr 10 '15 at 08:20
  • @Justin Liang, your class based header code will still violate the ODR if included in multiple files, unless the function definitions are inside the body of the class. – Hari Jun 11 '15 at 07:36
  • So, if my static member definition is preceded by `template ` then it may go into a header, and if it's `template<>` then it may not? – Violet Giraffe Jun 29 '16 at 08:27
60

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.

FluxLemur
  • 1,827
  • 16
  • 18
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
26

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.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Actually there is a 3rd way which is to put those in a no name namespace... which is similar to having static in C. – Alexis Wilke Feb 11 '14 at 23:26
  • 4
    That's not valid here. A template specialization needs to be in the same namespace as the original template. – Edward Strange Feb 12 '14 at 00:50
  • Also, unnamed namespaces in headers are themselves sources of One Definition Rule violations, and thus recommended against by the CppCoreGuidelines: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-unnamed – mabraham Jan 07 '21 at 13:22
1

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.

Francis
  • 737
  • 1
  • 7
  • 21