Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template method specialization linking error

Consider the following header and source files:

// main.cpp
#include "myClass.h"

int main()
{
  MyClass m;
  m.foo<double>();
  m.foo<float>();
}

// myClass.h
#pragma once

#include <iostream>

using namespace std;

class MyClass
{
public:

  template <typename T>
  void foo()
  {
    cout << "Template function<T> called" << endl;
  }

  template <>
  void foo<int>()
  {
    cout << "Template function<int> called" << endl;
  }

  template <>
  void foo<float>();

};

// myClass.cpp
#include "myClass.h"

template <>
void MyClass::foo<float>()
{
  cout << "Template function<float> called" << endl;
}

I get a linking error wrt the foo<float> specialization. If I place the definition of the specialization in the header file, then everything works as expected.

I figured that the reason may be that the method is not explicitly instantiated (although full specialization of template class does not need an explicit instantiation for proper linking). If I try to explicitly instantiate the method, I get this error:

error C3416: 'MyClass::foo' : an explicit specialization may not be explicitly instantiated

So the questions are:

  • Is there any way to define the specialization in the cpp file and link properly?
  • If not, why not? I can explicitly instantiate template methods that are not specialized just fine. Why not the same for full specializations?
like image 549
Samaursa Avatar asked Dec 29 '12 03:12

Samaursa


1 Answers

Although WhozCraig's answer (now deleted) provides the correct code to solve your problem, here are some direct answers to your questions, including comments on your code:

  1. foo<int>() and foo<float>() are explicit specializations of a member template. Those must not appear inside the definition of the class they belong to. The Standard says:

    (§14.7.3/3) [...] The definition of a class or class template shall precede the declaration of an explicit specialization for a member template of the class or class template. [...]

    So you must put them after the class definition.

  2. In the case of foo<int>, which is fully defined in the header file, this implies that you must put the word inline before the definition; otherwise you will get into trouble with the linker if the header file is included in more than one translation unit.

  3. The specialization for foo<float>() is defined in a separate file that is later linked to main.cpp. This is possible, but it requires that a declaration of it be given in the header file (you do this already, but you must do it outside the class definition):

      template <>
      void MyClass::foo<float>();
    

    This is required because of another statement in the Standard:

    (§14.7.3/6) 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. [...]

  4. Since all specializations are explicit (i.e. full specializations, to use your word), there is no need for explicit instantiations, but they are possible (§14.7.2). You would place them at the end of the .cpp file, and the syntax would be:

    template void MyClass::foo<float>();
    template void MyClass::foo<int>();
    

    Again, this is only really useful for types that do not have their own explicit specializations.

The correct code for the header and implementation file therefore looks like this:

.h file:

class MyClass
{
public:
  template <typename T> void foo()
  { cout << "Template function<T> called" << endl; }
};

template <> inline void MyClass::foo<int>()
{ cout << "Template function<int> called" << endl; }

template <> void MyClass::foo<float>();

.cpp:

#include "myClass.h"

template <> void MyClass::foo<float>()
{ cout << "Template function<float> called" << endl; }

/* This is unnecessary for float, but may be useful for
   types that do not have their own explicit specializations: */
template void MyClass::foo<float>();
like image 140
jogojapan Avatar answered Sep 29 '22 18:09

jogojapan