Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the right way to specialize a template when using "extern template"?

I am hoping someone can point out the correct way to specialize a method in a template class while using "extern template class" and "template class" for explicit instantiation with gnu c++. I've tried to boil down this problem with the simplest example that mimics my real problem. It appears that declaring "extern template" implies a template instantiation which causes errors when specializing methods. Given a driver program:

main.cc

#include A_H #include <iostream>  int main() {     A<int> ai;     A<long> al;      std::cout << "ai=" << ai.get() << " al=" << al.get() << std::endl;      return 0; } 

And the following implemntation of A

a.h

template<typename T> struct A {     int get() const; };  extern template class A<int>; extern template class A<long>; 

a.cc

#include "a.h"  template<typename T> int A<T>::get() const {     return 0; }  template<> int A<long>::get() const {     return 1; }  template class A<int>; template class A<long>; 

I receive the following error when compiling with either, g++ 4.1.2 or 4.4.4

 % g++ -Wall -g -D'A_H="a.h"' a.cc main.cc           a.cc:10: error: specialization of 'int A<T>::get() const [with T = long int]' after instantiation  % 

If I comment out the two "extern template" lines in a.h, things compile and work as expected with both compilers. I assume depending on the existence of an explicit instantiation in the absence of "extern template" is unspecified behavior even in C++0x, otherwise, what's the point of C++0x adding "extern template"?

If I instead implement A as:

a-hack.h

template<typename T> struct A {     int get() const; };  template<typename T> int A<T>::get() const {     return 0; }  template<> inline int A<long>::get() const {     return 1; }  extern template class A<int>; extern template class A<long>; 

a-hack.cc

#include "a-hack.h"  template class A<int>; template class A<long>; 

and compile again, this works as expected

% g++ -Wall -g -D'A_H="a-hack.h"' a-hack.cc main.cc % ./a.out  ai=0 al=1 

However, in my real world example, this causes a program crash with g++ 4.1.2 (while working for g++ 4.4.4). I have not narrowed down the exact cause of the crash (segmentation fault). It only appears as if the stack pointer is corrupted within what would be the call to A<>::get().

I realize that explicit template instantiation is non-standard at this point, but would anyone expect what I've done above to work? If not, what is the correct way to do this?

Thanks

like image 636
Roland Avatar asked Mar 18 '11 18:03

Roland


People also ask

What is extern template?

Extern templatesA template specialization can be explicitly declared as a way to suppress multiple instantiations. For example: #include "MyVector. h" extern template class MyVector<int>; // Suppresses implicit instantiation below --

What are the two types of templates?

There are two types of templates in C++, function templates and class templates.

Which function is useful when template of template is used?

Explanation: As a template feature allows you to write generic programs. therefore a template function works with any type of data whereas normal function works with the specific types mentioned while writing a program.

Why does the need of template specialization arise?

This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.


2 Answers

extern template class A<long>; 

This line says that A<long> is to be explicitly instantiated according to the definitions the compiler has already seen. When you add a specialization later, you break that meaning.

Add a declaration of your specialization to the header file.

template <typename T> struct A { /*...*/ }; template<> int A<long>::get() const; extern template class A<int>; extern template class A<long>; 

In general, it's best to put as many specialization declarations as possible in the same header file as the primary template, to reduce surprises for the compiler about which declaration should be used for any particular instantiation.


Notice that the extern template declaration isn't necessary if you're dealing with a single template entity (as opposed to this case, where we have to instruct the compiler about both the class A<long> and the function A<long>::get()). If you want to specialize a function template in another translation unit, it suffices to write just template<>.

template<typename T> int freeGet() { return 0; }  // you can even add "inline" here safely! template<> int freeGet<long>();  // this function is not inline (14.7.3/12) 

But you must have the <> there. If you omit the <>, the declaration turns into an explicit instantiation of the default implementation (return 0), which is likely not what you wanted! Even if you add extern, the compiler is allowed to inline that default implementation; if your code unexpectedly breaks when you pass -O2, you might have accidentally omitted the <> somewhere.

like image 176
aschepler Avatar answered Sep 22 '22 12:09

aschepler


Adding this answer to address the question in the title (template instantiation, and not necessarily template method instantiation).

This much resembles function declaration/definition.

  • Explicit instantiation declaration: extern template class is a declaration and should generally go in the header.
  • Explicit instantiation definition: template class is a definition and should generally go in the cpp.
  • No code is generated in object files following the declaration.
  • Code is generated in the object file of the cpp that contained the definition.
  • Without explicit instantiation, implicit instantiation will take place when the template is actually used. This will happen in every compilation unit (object file) that uses the template.
  • Implicit instantiation won't take place if a declaration was encountered. This is the gist of this mechanism - avoid object code duplication caused by implicit instantiation that happened because the compiler didn't trust that there's a single compilation unit in charge of instantiating the template.

More info here.

like image 36
Michael Litvin Avatar answered Sep 24 '22 12:09

Michael Litvin