How to select the right overloaded function template at compile-time?

1.6k views Asked by At

I'm trying to understand how to select the right overloaded function template at compile-time, but the compiler is giving me a hard time. I can make it work, but I don't understand what is going on. Let me explain.

I have two structs A and B like below. One has a special function and the other a normal.

struct A
{
    void special() { std::cout << "special called.\n"; }
};

struct B
{
    void normal() { std::cout << "normal called.\n"; }
};

My intension is to have a mechanism, which at compile-time selects the right overloaded function template depending on if the special function is available. I have two functions run, which take the struct as a parameter, so they can call the appropriate function.

template<class Func, Func f> struct Sfinae {};

template <typename U>
static void run(U& u, Sfinae<void (U::*)(), &U::special>* = 0)
{
    u.special();
}

template <typename U>
static void run(U& u, ...)
{
    u.normal();
}

I've tested this with the following, with various results:

int main()
{
    A a;
    run<A>(a, 0); // works
    run<A>(a); // ERROR: ambiguous overloaded function
    run(a, 0); // ERROR: A has no member normal
    run(a); // ERROR: ambiguous overloaded function

    B b;
    run<B>(b, 0); // works
    run<B>(b); // works
    run(b, 0); // works
    run(b); // works

    return 0;
}

I'd like to use it as run(a) without any extra argument or <>. Is there something wrong with my code when this is not working?

Also, I'm interested to understand what is going on here and why this is deducing things like this, so I need to give <A> for A but not for B? I don't know what the standard says and if this is different between compilers, but at least gcc4.4.4 on Linux and gcc 4.0.1 on Mac work like I've described.

Can someone please shed some light on this? Thanks!

2

There are 2 answers

1
Mikael Persson On BEST ANSWER

This here will work. It sort-of assumes that the two functions normal and special are mutually exclusive (i.e. a class that has one of them doesn't have the other). I'm sure you can adapt it to your purpose. This uses boost::enable_if, of course.

#include <iostream>
#include <boost/utility/enable_if.hpp>

struct A
{
    void special() { std::cout << "special called.\n"; }
};

struct B
{
    void normal() { std::cout << "normal called.\n"; }
};

template<int> struct Sfinae { enum { value = true }; };

template <typename U>
static typename boost::enable_if<Sfinae<sizeof(&U::special)>,void>::type run(U& u)
{
    u.special();
}

template <typename U>
static typename boost::enable_if<Sfinae<sizeof(&U::normal)>,void>::type run(U& u)
{
    u.normal();
}


int main()
{
    A a;
    run(a); // works

    B b;
    run(b); // works

    return 0;
}

This works on gcc 4.6.0 on Linux.

1
Sarfaraz Nawaz On

For this particular situation you can do this, which is very simple:

template <typename U>
static void run(U & u)
{
    u.special();
}

template <>
static void run<B>(B &u)
{
    u.normal();
}

Or, you can simply remove template, and write two overloaded functions. I agree, this doesn't solve it in more general way.

Maybe, this topic will help you finding a general solution:

Is it possible to write a template to check for a function's existence?

See Johannes's answer. :-)