Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No generated code for explicitly specialized template even with explicit instantiation

I'm getting consistent behavior from both gcc 4.8.3 and clang 3.2, but do not understand why it is happening. Despite the fact that I have an explicit instantiation for a class template, the code is not being generated and I get an undefined symbol when I am using a fully specialized instance of the template.

I have a simple class template definition in a file 'temp.hpp'

#pragma once

template <typename T1>
class C 
{
public:
  C (T1 c) : d_c(c) {};
  ~C () = default;

  void print ();
private:
  T1 d_c;
};

Note that the method 'print()' is declared, but not defined here. I want the definition in the .cpp file and it will be specialized for different types.

So in the temp.cpp file I have the default definition of the print() method

#include "temp.hpp"
#include <iostream>

template<typename T1>
void
C<T1>::print ()
{
  std::cout << "Printing: " << d_c << std::endl;
}

followed by a specialization of the class for the type 'float':

template <>
class C <float>
{
public:
  C (float f) : d_f(f) {};
  ~C () = default;

  void print ()
  {
    std::cout << "float: " << d_f << std::endl;
  }

private:
  float d_f;
};

and since the definitions are in the .cpp file I must explicitly instantiate all the specializations that I will be using. So I have:

template class C<int>;
template class C<float>;

The driver for my test looks like this in test.cpp:

#include "temp.hpp"

int main()
{
  int i = 1;
  C<int> c_int(i);

  float f = 1.2;
  C<float> c_float(f);

  c_int.print();
  c_float.print();
}

Upon compiling and linking this I get error:

test.cpp: undefined reference to `C<float>::print()'

The object code for the C< int > is properly generated. I can see it using nm:

nm -C temp.o
...
0000000000000000 W C<int>::print()
0000000000000000 W C<int>::C(int)
0000000000000000 W C<int>::C(int)
...

As I mentioned earlier, this is consistent with gcc and clang so I'm assuming there is some language rule I don't understand here.

Note that if I add a usage of the print() method in file temp.cpp, then the code is generated, but that is silly and in my real code would be impossible. For this simple test case it would look like:

void foo () 
{
  C<float> s(1.3);
  s.print();
}

In the real code which motivated this little test my template has 3 template arguments which combine to expand into about 30 permutations of the code. There are one or two of those for which I need a specialization which does something different, but the other 28 I can leave alone.

Any pointers on where I've gone wrong or a language reference for why the explicit instantiation of should not generate code are greatly appreciated. I've spent 1/2 a day reading all the other stackoverflow posts on explicit instantiation and believe I am using it correctly.

like image 534
Jay Avatar asked Jul 31 '15 16:07

Jay


1 Answers

From [temp.expl.spec]:

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

We're explicitly specializing C in temp.cpp, but in test.cpp, it is not declared before it is used. Thus, your code is ill-formed, no diagnostic required. You'll have to simply move the declaration of C<float> into temp.hpp

Always be careful with explicit specializations. The standard takes them very seriously:

The placement of explicit specialization declarations for function templates, class templates, [...], can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

like image 63
Barry Avatar answered Oct 15 '22 14:10

Barry