15
votes

When I specialize a (static) member function/constant in a template class, I'm confused as to where the declaration is meant to go.

Here's an example of what I what to do - yoinked directly from IBM's reference on template specialization:

===IBM Member Specialization Example===

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

int main() {
   X<char*> a, b;
   X<float> c;
   c.f(10); // X<float>::v now set to 20
}

The question is, how do I divide this into header/cpp files? The generic implementation is obviously in the header, but what about the specialization?

It can't go in the header file, because it's concrete, leading to multiple definition. But if it goes into the .cpp file, is code which calls X::f() aware of the specialization, or might it rely on the generic X::f()?

So far I've got the specialization in the .cpp only, with no declaration in the header. I'm not having trouble compiling or even running my code (on gcc, don't remember the version at the moment), and it behaves as expected - recognizing the specialization. But A) I'm not sure this is correct, and I'd like to know what is, and B) my Doxygen documentation comes out wonky and very misleading (more on that in a moment a later question).

What seems most natural to me would be something like this, declaring the specialization in the header and defining it in the .cpp:

===XClass.hpp===

#ifndef XCLASS_HPP
#define XCLASS_HPP

template<class T> class X {
public:
   static T v;
   static void f(T);
};

template<class T> T X<T>::v = 0;
template<class T> void X<T>::f(T arg) { v = arg; }

/* declaration of specialized functions */
template<> char* X<char*>::v;
template<> void X<float>::f(float arg);

#endif

===XClass.cpp===

#include <XClass.hpp>

/* concrete implementation of specialized functions */
template<> char* X<char*>::v = "Hello";
template<> void X<float>::f(float arg) { v = arg * 2; }

...but I have no idea if this is correct. Any ideas?

3

3 Answers

11
votes

Usually you'd just define the specializations inline in the header as dirkgently said.

You can define specializations in seperate translation units though if you are worried about compilation times or code bloat:

// x.h:
template<class T> struct X {
    void f() {}
}

// declare specialization X<int>::f() to exist somewhere: 
template<> void X<int>::f();

// translation unit with definition for X<int>::f():
#include "x.h"
template<> void X<int>::f() {
    // ...
}

So yes, your approach looks fine. Note that you can only do this with full specializations, thus it is often impractical to do this.

For details, see e.g. Comeaus template FAQ.

4
votes

Put them all in a hpp file. Make the specializations and anything you define outside the class inline -- that'll take care of multiple definitions.

1
votes

To answer one of your questions: is code which calls X::f() aware of the specialization, or might it rely on the generic X::f()?

If the compiler sees a definition which matches its requirements, then it will use it. Else it will generate normal function calls.

In your first code snippet, you supply a generic definition for X<T>::f(T arg), so the compiler will instantiate that for any T apart from float.

If you were to omit the generic definition, then the compiler would generate calls to, say, X<double>::f(double) and the linker would go searching for the definition which might end with a linker error.

To summarise: You can have everything in headers, because as templates you won't get multiple definitions. If you only have declarations, you will need definitions elsewhere for the linker to find later.