2

Suppose I have a class template template <typename T> class X

Is it somehow possible to use type traits or a similar technique to call a (static) method of T but only if the type T declares such method, e.g. something like this:

template <typename T> 
class X {
    static void foo(){
       if(has_method(T,bar)) //Something like this      
         T::bar(); //If T has no bar() method, then foo does nothing
    }

};
gexicide
  • 35,369
  • 19
  • 80
  • 136
  • 3
    Search for "tag dispatching" and take a look at [this](http://stackoverflow.com/q/257288/500104). – Xeo Feb 04 '13 at 15:18

2 Answers2

3
template <typename T>
class X {
public:
    static void foo() {
        foo_impl(static_cast<T*>(nullptr));
    }
private:
    // foo_impl #1
    template <typename U>
    static auto foo_impl(U*) -> decltype(U::bar(), void()) {
        U::bar();
    }

    // foo_impl #2
    static void foo_impl(...) {}
};

Because of the SFINAE rule, foo_impl #1 is not in the overload set when U::bar() is not a valid expression, and foo_impl #2 gets called instead. If type deduction does succeed for foo_impl #1, it will always be a better conversion than the ellipsis.

Ideone demo: http://ideone.com/UKVmIB

aschepler
  • 65,919
  • 8
  • 93
  • 144
2

First, you'll want to make the decision at compile time, since otherwise, you'll need to provide the function, even if you never go into the branch. I can imagine something like:

template <typename T, void (T::*)() > struct HasBar;

template <typename T>
void doBar( HasBar<&T::bar>* ) { T::bar(); }

template <typename T>
void doBar( ... ) {}

template <typename T>
class X
{
    static void f()
    {
        doBar<T>( 0 );
    }
};

It's a more or less classic trick; if &T::bar fails (because T doesn't have a member bar), then the instantiation of doBar( HasBar<...>) fails, the function is not added to the overload set, so the other one is called. If &T::bar is a legal expression, both function templates can be successfully instantiated, and the conversion of 0 to a pointer is chosen before a match with ... (which is always the last resort when determining function overload resolution).

EDIT:

I missed the fact that the function was static. The above is for a non-static function. For a static function, change the first line to:

template <typename T, void (*)() struct HasBar;

The rest should work as is.

James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • What is `void (&T::bar)()`? – aschepler Feb 04 '13 at 15:33
  • @aschepler An error:-). When you take the address, it should be just `&T::bar`. I'll correct it. – James Kanze Feb 04 '13 at 15:36
  • Using function pointers disqualifies functions like `static void bar(int n=0);`. (But I don't think there's any solution for that using only C++03.) – aschepler Feb 04 '13 at 15:39
  • @aschepler Good point. But in the classes you're instantiating it over, you can always overload: just add `static void bar() { bar(0); }` and drop the default argument. – James Kanze Feb 04 '13 at 15:47