5

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.

deft_code
  • 57,255
  • 29
  • 141
  • 224
ted
  • 4,791
  • 5
  • 38
  • 84
  • 1
    It might be worth stating which version of gcc you are using and any additional compiler flags you are using. the compiler flags might account for any code bloat. Could you also post a cut down sources for the 2 files, that would enable people to rty it out for themselves. – mark Dec 19 '11 at 15:08
  • As I am not at the right pc right now I can just tell you this, I am using gcc that comes packaged with ubuntu 11.10 from codeblocks. I will write the actual version number and some example code as soon as I am back to the other pc. Hoped I had missed something obvious. – ted Dec 19 '11 at 16:54

1 Answers1

5

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();
}
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • Thank you so much, I just came home and was about to write it up. I am going to report that bug. By the way the code bloat is still curious to me. I am actually using all my class functions in the example as it is, thus i would not expect additional code. And in addition o that: Is there no way the linker can throw unused functions out? P.S.: Are you one of the gurus behind gcc? – ted Dec 19 '11 at 22:41
  • For some reason I cant reproduce the bug, meaning I am putting a minmal example together and it compiles fine. Can you post your example code please? – ted Dec 19 '11 at 23:13
  • The linker will throw out your unused functions unless you tell it not to. `.so`s keep everything unless it's marked internal. But if you link it into a binary all the unused symbols should be gone. To catch the missing symbols I had to inspect the `.o` files. – deft_code Dec 19 '11 at 23:42
  • thank you very much, in case you care here is my bugreport: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51629 After my last answer I thougth of reducing my code in stead of starting from scratch and got there too – ted Dec 20 '11 at 00:09
  • I believe I may have encountered this same bug with gcc 4.7.2, but with copy constructors and copy & move assignment operators instead of destructor. FWIW reducing the inline default-definition to just a declaration, and declaring the function as defaulted out of line fixes the problem for me. – boycy Sep 03 '13 at 13:26
  • This issue appears to also affect G++ 4.8.4, at least with move constructors defaulted. – Isac Casapu Mar 07 '16 at 18:00