views:

138

answers:

2

Hallo!

I would like to specialise only one of two template types. E.g. template <typename A, typename B> class X should have a special implementation for a single function X<float, sometype>::someFunc().

Sample code:

main.h:

#include <iostream>

template <typename F, typename I>
class B
{
public:
    void someFunc()
    {
        std::cout << "normal" << std::endl;
    };

    void someFuncNotSpecial()
    {
        std::cout << "normal" << std::endl;
    };
};

template <typename I>
void B<float, I>::someFunc();

main.cpp:

#include <iostream>
#include "main.h"

using namespace std;

template <typename I>
void B<float, I>::someFunc()
{
    cout << "special" << endl;
}

int main(int argc, char *argv[])
{
    B<int, int> b1;
    b1.someFunc();
    b1.someFuncNotSpecial();

    B<float, int> b2;
    b2.someFunc();
    b2.someFuncNotSpecial();
}

Compilation fails for class B. Is it true, that this is not possible in C++ in this way? What would be the best workaround?

[edit]

template <float, typename I> void B<float, I>::someFunc(); leads to main.h:26: error: ‘float’ is not a valid type for a template constant parameter

template <typename I> void B<float, I>::someFunc(); leads to main.h:27: error: invalid use of incomplete type ‘class B’

And I'm using gcc.

[edit]

I don't want to specialise the whole class, as there are other functions that don't have a specialisation.

+5  A: 

You have to provide a partial specialization of the class template B:

template <typename I>
class B<float, I>
{
public:
    void someFunc();
};

template <typename I>
void B<float, I>::someFunc()
{
    ...
}

You can also just define someFunc inside the specialization.

However, if you only want to specialize a function, and not a class do e. g.

template <typename F, typename I>
void someFunc(F f, I i) { someFuncImpl::act(f, i); }

template <typename F, typename I>
struct someFuncImpl { static void act(F f, I i) { ... } };

// Partial specialization
template <typename I>
struct someFuncImpl<float, I> { static void act(float f, I i) { ... } };

But you can't specialize a function template without this trick.

Alexandre C.
You couldn't know this. But in my use case there are lots of other functions that don't have a specialisation. With this approach I would have to double all these functions.
tauran
@tauran: You cannot provide partial specializations for function templates. Only for class templates, and you have to provide the definition again for the whole class. Live with it, or see updated answer.
Alexandre C.
Could you please address one more thing in tauran's code? There is `template <typename I> void B<float, I>::someFunc();` at the end of `main.h`. I think tauran wants to declare, that there is a specialization defined somewhere, in another compilation unit. Doesn't such thing require exported templates? If the specialization is meant, to be visible in all compilation units, `main.h` is included in, doesn't it have to be in the header?
Maciej Hehl
@Maciej: Exported template don't exist (except in the standard). Everything which is not an explicit (full) specialization has to be declared in the header.
Alexandre C.
@Maciej it's worse because his attempt tries to declare a member function of a class template partial specialization `B<float, I>`. Such a partial specialization does not exist, so that thing at the end of `main.h` is ill-formed. (Even if it would exist, it would be ill-formed because an out-of-class declaration of a function must be a definition).
Johannes Schaub - litb
Are you sure about that? Maybe I made an error but the last time I did it, I ended up putting the declaration for a specialized function in the .h file and the definition in a .cpp file (gcc). gcc would not let me put the specialized function in the .h file and without the delaration there it would ignore the specialized version in the .cpp file...
tauran
+2  A: 

Although you can totally specialize member functions of a class template, you cannot _partially specialize member functions. - Andrei Alexandrescu

Partial Class specialization is explained by the other posters.

You can, however, use overloading:

template <class T, class U> T fun(U obj); // primary template
template <class U> void Fun<void, U>(U obj); // illegal pertial
// specialization
template <class T> T fun (Window obj); // legal (overloading)

If you want to go deep into this, you can read about this issue in depth in "Modern C++ Design" by A. Alexandrescu.

Gabriel Schreiber
+1 for recalling template overloads. This is a tool I seldom think about using, but which does the job in some situations.
Alexandre C.