Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linker error when using an extern template

I have working code with a template. Similar to the stl::string I am mostly using my template with one parameter across multiple compilation units. To save time I am trying to use extern instantiation. However changing the lines as follows yields an error. What is the right way to do it? (P.S. Compiling on gcc with the c++0x flag)

typedef myTemplate_base<commonType> myTemplate;
extern template class myTemplate_base<commonType>; //using "extern template myTemplate" wont work

I added an extra cpp file with the following to the project.

template class myTemplate_base<commonType>;

The linker comes up with this error messge (giving the line of the first object instantiation (myTemplate someVar;) in the main file as error source):

undefined reference 'myTemplate_base::~myTemplate_base()'

However this type is in the class with the following definition ~myTemplate() = default;

Edit: If you have a better title in mind please comment, so the right people take a look at this

Edit2: There is on funny thing, the addition of template class myTemplate_base<commonType> increases the executable size tremendously (+100k on a 450k binary) even though the template is used in the main (to compile I have to comment the extern part out). This hints that the linker keeps two implementations of a template with the same instantiation/me overlooking something.

like image 913
ted Avatar asked Dec 19 '11 11:12

ted


1 Answers

You have found a compiler bug. I reproduced it with g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1. The work around is to leave the destructor as implicitly default. The linker error only arises when the destructor is explicitly marked it as default (= default).

Either way the destructor is never produced with the extern template. Marking a template class as extern the compiler notes that any needed symbols are extern. Except the destructor it's still defined in the file. It looks like adding an = default with an extern template confuses the compiler into thinking the destructor will be defined elsewhere.

The code bloat is caused by the extern template. The compiler only instantiates the methods of the a template class that are actually used. That is usually much less than the number that are defined. When you use force instantiate a class with extern template the compiler emits code for all of the methods (except for the destructor as you just discovered).


g++ -c -std=c++0x main.cpp
g++ -c -std=c++0x extern.cpp
g++ main.o extern.o

header.hpp

#pragma once

#include <iostream>
#include <string>
#include <typeinfo>

template< typename T >
class Foo
{
public:
   Foo( void ) :
      m_name( typeid(T).name() ),
      m_t( T() )
   { }

   // add or remove this to cause the error
   ~Foo( void ) = default;

   void printer( void )
   {
      std::cout << m_name << std::endl;
   }

   T returner( void )
   {
      return m_t;;
   }

private:
   std::string m_name;
   T m_t;
};

extern template class Foo<int>;

extern.cpp

#include "header.hpp"

template class Foo<int>;

main.cpp

#include "header.hpp"

int main()
{
   Foo<int> fi;
   fi.printer();

   Foo<float> ff;
   ff.printer();
}
like image 196
deft_code Avatar answered Sep 24 '22 21:09

deft_code