Search code examples
c++c++11templates

Extern Templates vs inline in .h


I have the following code example:

--- include.h ---
#ifndef EXTERN_TEMPLATE_H
#define EXTERN_TEMPLATE_H

template <typename T>
class MyTemplate {
public:
    void print(const T& t) 
};

template <typename T>
void foo();

extern template class MyTemplate<int>;
extern template void foo<double>();

#endif

.

--- src.cpp ---
#include "include.h"
#include <iostream>

template <typename T>
void MyTemplate<T>::print(const T& t) {
    std::cout << t << std::endl;
}

template <typename T>
void foo() {
    std::cout << "Hello!" << std::endl;
}

template class MyTemplate<int>;
template void foo<double>();

.

--- main.cpp ---
#include "include.h"

int main() {

    MyTemplate<int> myInstance;
    myInstance.print(100);

    foo<double>();

    return 0;
}

.

--- temp.cpp ---
#include "include.h"

void foo() {
    MyTemplate<int> myInstance;
    myInstance.print(200);

    foo<double>();
}

.

--- temp2.cpp ---
#include "include.h"

void foo2() {
    MyTemplate<int> myInstance;
    myInstance.print(300);

    foo<double>();
}

Now, if in Linux I do:

nm -g -C --defined-only *.o

I will get something like:

src.cpp.o:
0000000000000000 W void foo<double>()
0000000000000000 W MyTemplate<int>::print(int const&)

main.cpp.o:
0000000000000000 T main

temp.cpp.o:
0000000000000000 T foo()

temp2.cpp.o:
0000000000000000 T foo2()

Which looks fine because this is exactly what I want to achieve: reuse the template instantiation from elsewhere.


Now, I know that:

The extern keyword in the specialization only applies to member functions defined outside of the body of the class. Functions defined inside the class declaration are considered inline functions and are always instantiated.

So, I tried this code:

--- include.h ---
#ifndef EXTERN_TEMPLATE_H
#define EXTERN_TEMPLATE_H
#include <iostream>

template <typename T>
class MyTemplate {
public:
    void print(const T& t) {
        std::cout << t << std::endl;
    }
};

template <typename T>
void foo() {
    std::cout << "Hello!" << std::endl;
}

extern template class MyTemplate<int>;
extern template void foo<double>();

#endif

.

--- src.cpp ---
#include "include.h"

template class MyTemplate<int>;
template void foo<double>();

main.cpp, temp.cpp and temp2.cpp are the same.

Now, if I do: nm -g -C --defined-only *.o I will get:

src.cpp.o:
0000000000000000 W void foo<double>()
0000000000000000 W MyTemplate<int>::print(int const&)

main.cpp.o:
0000000000000000 T main

temp.cpp.o:
0000000000000000 T foo()

temp2.cpp.o:
0000000000000000 T foo2()

Which is the same as the first example, which seems to me a little bit counterintuitive.

Now, my question is: Is there anything wrong with my code example (not correctly implemented), or is this the normal, expected behavior?

Note: I tried to compile with O0, O2, O3 and I have got the same results.

NOTE2: So, after further reading I understand that even though the default behavior for functions defined inside the class declaration is "inline", by adding the "extern" part we are overriding somehow this behavior?


Solution

  • Which is the same as the first example, which seems to me a little bit counterintuitive.

    With those two lines (which are present in both examples):

    extern template class MyTemplate<int>;
    extern template void foo<double>();
    

    You said to compiler: this templates instance will be instantiated in some other place. So when any translation unit seen this (when including this header file) template instantiation was not generated.

    With those two lines:

    template class MyTemplate<int>;
    template void foo<double>();
    

    in cpp file you said to compiler instantiate this this template in current translation unit.

    Both examples have this, so result in object files is same.

    In second example just drop:

    extern template class MyTemplate<int>;
    extern template void foo<double>();
    

    And you will see different result. Multiple object files will contain same template instantiation. Basically src.o will have instance of this template because of template class MyTemplate<int>; and other object file which are using this template will have it too.