11
votes

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?
1
@billz: This is not a duplicate (or I am bad at searching... but I searched with my colleagues for quite some time). There are many questions regarding class specialization and template function specializations but not when the class is a non-template (does that matter?) and the template method is a full specialization.Samaursa
@billz: With regards to your duplicate link, that is a general question about linking errors with templates. I am already aware of the tricks to explicitly instantiate a class template and template methods. The problem here is full specialization of a template method.Samaursa
Shows how antiquated my toolchain is, apparently (Apple LLVM 4.1, which is supposed to be C++11 compliant), because the header file itself in this code won't compile for me; errors with ("Explicit specialization of 'foo' in class scope") on the first in-class specialization.WhozCraig
@WhozCraig Gcc 4.7.2 won't compile either but VS2012 compilesbillz
@didierc: It is definitely possible to put the definition of a template in a separate translation unit. All you need to do is explicitly instantiate it: stackoverflow.com/a/4933205/368599 Incidentally, this is what is confusing me, hence the question. Why is the fully specialized template method exempt from such rules?Samaursa

1 Answers

17
votes

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>();