hr0m
hr0m

Reputation: 2805

Linking of template object files - different behavior with clang and gcc

There has been a lot questions about different behavior between gcc and clang. However i did not find the solution for my problem yet.

I use templates and i want to spread the definition and implementation of the class. I read about it (a lot) i am aware of the different possibilities. I choose the specific declaration, which templates i want to support. With:

   template class Temp<char>;
   template class Temp<double>;

I choose which templates i support, which actually works, if i put those lines at the end of the template.cc file. However with gcc i also can write it in the header file. Clang does not support it, i get linking errors.

But why? What is wrong with the declaration of used templates in the header file???

This is my toy example:

template.hh

#pragma once
#include <iostream>

template<typename T>
class Temp
{
public:
    Temp(T data);
    virtual ~Temp (){};
    void print();

private:
    T data_;
};

//Only gcc can support this
//template class Temp<char>;
//template class Temp<double> 

template.cc

#include "template.hh"                                                                                           

template<typename T>
Temp<T>::Temp(T data): data_(data)
{
}

template<typename T>
void Temp<T>::print()
{
    std::cout << data_ << " " << sizeof(data_) << std::endl;
}

//Delete those, if it is used in header
template class Temp<char>;
template class Temp<double>;

test.cc

#include "template.hh"

int main(int argc, char *argv[])
{
    Temp<char> temp = Temp<char>('c');
    temp.print();

    Temp<double> temp2 = Temp<double>(1.0);
    temp2.print();
    return 0;
}

Desired output:

c  1
1  8

Upvotes: 1

Views: 210

Answers (2)

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153802

An explicit instantiation of a function template is a definition. Explicit instantiation of a class template is a short-hand for instantiating all [non-template] member functions of the class template. Having multiple definitions of an entity in C++ results in a violation of the ODR-rule (One Definition Rules). Violations of the ODR-rule do not need to be diagnosed.

As a result, explicit instantiations of template belong into a ".cc" file. If you want to declare that a template will be explicitly instantiated in some translation unit, you can use extern template declarations, e.g.:

template <typename> void f();
template <typename> class F { /*...*/ };

extern template void f<int>();
extern template class F<int>();

extern template declarations allow templates to be defined in the header but inhibit implicit instantiation. Instead, the extern template declaration promises that there will be one translation unit providing an explicit instantiation.

Upvotes: 2

John Zwinck
John Zwinck

Reputation: 249123

Explicit template instantiation belongs in the ".cc" implementation file, not the header. If you want to declare it in the header, you can do so using extern:

extern template class Temp<char>;
extern template class Temp<double>;

This will avoid the multiply-defined symbols that you are probably experiencing with Clang otherwise.

It may be that GCC supports explicit instantiation in the header file, but this does not mean it is correct C++, only that GCC is being liberal in this case. Don't rely on that.

Upvotes: 3

Related Questions