Declaration of template class member specialization
Asked Answered
A

4

15

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?

Avicenna answered 23/3, 2010 at 8:28 Comment(0)
B
11

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.

Beaded answered 23/3, 2010 at 12:46 Comment(5)
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.Avicenna
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?Beaded
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?Avicenna
When compilation times or code bloat become an issue, i can't think of much else.Beaded
Hi Georg, I come across this problem with visual studio 2015: developercommunity.visualstudio.com/content/problem/143189/… , do you know if there's any solution to it now?Dirty
V
4

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.

Volatilize answered 23/3, 2010 at 8:34 Comment(10)
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"?Avicenna
@Ziv: I thought that static variables wouldn't work in headers, but I've tried this in VS2008 and it seems to work.Watteau
The specialisations can be defined in either a source file or inline in a header, but must be declared in a header.Thundering
@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.Avicenna
@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?Avicenna
@Ziv: they look fine, assuming they compile.Thundering
@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.Watteau
@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.Watteau
@quamrana: inline is a function specifier.Beaded
@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 :)Avicenna
W
1

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.

Watteau answered 23/3, 2010 at 11:7 Comment(3)
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?Avicenna
@Ziv: My experiments, contrary to my expectations, say that everything can go in the header, static specialisations and all.Watteau
That is odd. I tried that, and I got a "multiply-defined..." error. Yay compiler conformity!Avicenna
O
0

Using template void X<float>::f(float arg) { v = arg * 2; } instead.

Overlap answered 31/12, 2021 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.