9

Suppose we have a class:

template <class Type>
class A
{
public:
    void function1(float a, Type b);
    void function1(float a, float b);
};

Now instantiate the class like this:

A<int> a;

It's fine, this class will have 2 overloaded functions with these parameters: (float a, int b); (float a, float b);

But when you instantiate the class like this:

A<float> a;

You get compile error:

member function redeclared.

So, depending on the type of Type, I wan't (or don't want) the compiler to define a function, something like this:

template <class Type>
class A
{
public:
    void function1(float a, Type b);

    #if Type != float
    void function1(float a, float b);
    #endif
};

But, of course, the syntax above doesn't work. Is it possible to perform such a task in C++? If possible, please provide an example.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Vadim
  • 1,223
  • 2
  • 17
  • 26

3 Answers3

9

You can use some C++11 std::enable_if :

template <class Type>
class A
{
public:
    template<typename t = Type, 
       typename std::enable_if<!std::is_same<t, float>::value, int>::type = 0>
    void function1(float a, Type b) {
    }

    void function1(float a, float b) {
    }
};
Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107
8

You could use template specialization:

template <class Type>
class A {
public:
    void function1(float a, Type b) {
    }
    void function1(float a, float b) {
    }
};

template <>
class A<float> {
public:
    void function1(float a, float b) {
    }
};

// ...

A<int> a_int;
a_int.function1(23.4f, 1);
a_int.function1(23.4f, 56.7f);

A<float> a_float;
a_float.function1(23.4f, 56.7f);

--- EDIT ---

If you have a large number of common functions, you could do something like this:

class AImp {
public:
    void function1(float a, float b) {
    }
    void function1(float a, double b) {
    }
    void function1(float a, const std::string& b) {
    }
    // Other functions...
};

template <class Type>
class A : public AImp {
public:
    void function1(float a, Type b) {
    }
    using AImp::function1;
};

template <>
class A<float> : public AImp {
};

// ...

A<int> a_int;
a_int.function1(23.4f, 1);
a_int.function1(23.4f, 56.7f);
a_int.function1(23.4f, 56.7);
a_int.function1(23.4f, "bar");

A<float> a_float;
a_float.function1(23.4f, 56.7f);
a_float.function1(23.4f, 56.7);
a_float.function1(23.4f, "bar");
Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167
  • If for example, I have 10 functions in a class, and only 2 or 3 need to be specialized, then I have to rewrite all 10 functions in a specialized version? – Vadim May 01 '12 at 09:47
  • @Vadim Essentially yes, but you don't have to repeat their bodies - you can always place a common implementation in a separate function and then just call _it_ from bodies of both functions (from both specialized and general class). – Branko Dimitrijevic May 01 '12 at 10:05
  • @Vadim: not necessarily, you could perhaps put the functions that don't need to be specialized into a common base class. Or the functions that *do* need to be specialized in a template base class. – Steve Jessop May 01 '12 at 10:20
  • @SteveJessop Yup, I was just writing about that - one only needs to be careful about overloading resolution (`using` in generic `A`). – Branko Dimitrijevic May 01 '12 at 10:22
  • @Vadim Or, you could use inheritance, provided you are aware of the strangeness of the C++ overloading resolution and don't forget `using` in `template A` - see my edit. – Branko Dimitrijevic May 01 '12 at 10:24
2

Use SFINAE:

#include <iostream>
#include <type_traits>

template <typename Type>
struct Foo {
    template <typename T = Type>
    void function1(float a, float b, typename std::enable_if<!std::is_same<T, float>::value>::type *c = 0) {
        std::cout << "float\n";
    }
    void function1(float a, Type b) {
       std::cout << "type\n";
    }
};

int main() {
    Foo<float> f;
    f.function1(1, 1);
    f.function1(1.0f,1.0f);
    Foo<int> g;
    g.function1(1,1);
    g.function1(1.0f,1.0f);
    g.function1(1.0,1.0); // warning!
}

Output:

type
type
type
float
type

You'll need C++11 mode, to allow the default template parameter in a function template. And also to get enable_if and is_same, although you could get enable_if from Boost instead.

The "warning!" is because with your original code g.function1(1.0,1.0); was ambiguous. Now the the non-template overload is preferred. You can make it ambiguous again by doing

    template <typename T = Type>
    void function1(float a, Type b, typename std::enable_if<true>::type *c = 0) {
       std::cout << "type\n";
    }
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699