views:

389

answers:

3

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?

Thanks much,

Ziv

+2  A: 

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.

dirkgently
Interesting. But this doesn't work for static variables - only for functions. That aside - isn't there a more elegant/standard way to deal with this? Template class member specialization is a major part of template programming. Surely "where do you put the declaration" has a more straightforward answer than "declare inline and fool the compiler"?
Ziv
@Ziv: I thought that static variables wouldn't work in headers, but I've tried this in VS2008 and it seems to work.
quamrana
The specialisations can be defined in either a source file or inline in a header, but must be declared in a header.
Mike Seymour
@quamrana: I didn't understand what you thought wouldn't work. "static inline int X = 1;" is what (as far as I can tell) doesn't work with dirkgently's answer.
Ziv
@Mike: Your comment sounds like what I'm looking for! Is my code snippet with the XClass.hpp/XClass.cpp titles the correct way of declaring them?
Ziv
@Ziv: they look fine, assuming they compile.
Mike Seymour
@Ziv: Your comment to me above does not use templates, so that wouldn't work. I've found that using templates, as in your first snippet, works.
quamrana
@Ziv: Under VS2008, `static inline int X=1;` doesn't compile, but `static int X = 1;` does, but each translation unit gets its own X.
quamrana
@quamrana: `inline` is a function specifier.
Georg Fritzsche
@quamrana: Oh, I see! You're saying: inline the functions, and define the static variables (because [I think] you're allowed to multiply-define a static variable), and the two of those together solve the problem. Thanks :)
Ziv
A: 

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.

quamrana
My question is the other way around: if/where/how to put the specialized declaration. A template specialization definition *can't* go in the header (unless inlined, as dirkgently suggested). How do I make sure that X<float>::f() calls the specialization, and doesn't mistakenly call the generic definition?
Ziv
@Ziv: My experiments, contrary to my expectations, say that everything *can* go in the header, static specialisations and all.
quamrana
@quamrana: That is odd. I tried that, and I got a "multiply-defined..." error. Yay compiler conformity!
Ziv
+3  A: 

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.

Georg Fritzsche
Question, out of curiousity: why the preference for inlining? It seems an odd "requirement" to impose - that "by default" a template specialization would be inlined. I ask all this only because you imply that that's what you would "usually" do, and I don't see why I'd want to usually do that any more than I'd want to usually declare any other function inline.
Ziv
Its about the preference for putting them in headers, `inline` is a side-effect. Less to write, more optimization opportunities, no errors by forgetting one declaration. Also you always need to fully specialize and for most templates that would mean to add one explicit declaration and instantiation for every combination of types it is used with. Kind of defeats the intent of genericity, doesn't it?
Georg Fritzsche
Well, of course I'm only talking about the cases when a full specialization is what's desired, otherwise it's obviously a whole different story... Flip the question, then: why wouldn't those same advantages lead us to prefer to usually write all functions inline?
Ziv
When compilation times or code bloat become an issue, i can't think of much else.
Georg Fritzsche