c++ – How to check if class T has a method foo?

Question:

Is it possible in C++ to write something similar to this:

template<class T>
void f(T a)  {
  if ( Существет метод a.foo ) {
    a.foo();
  } else {
    myfoo(a);
  }
}

Those. the function should behave differently depending on whether the class T has a foo method or not.

Answer:

This is done like this:

#include <iostream>
struct A{
    void foo(){}
};

struct B{};

template<class T>
struct Test{
    typedef void(T::*P)(void);

    template<class U, P = &U::foo>
    struct True{char dummy[2];};

    typedef char False;

    static False detect(...);

    template<class U>
    static True<U> detect(U*);

    static const bool exists = (sizeof(False) != sizeof(detect(static_cast<T*>(0))));
};


int main(){
    std::cout << std::boolalpha << Test<B>::exists << std::endl; //false
    std::cout << std::boolalpha << Test<A>::exists << std::endl; //true
}

Now about what is going on here. The code uses an idiom called SFINAE. The abbreviation SFINAE stands for substitution failure is not an error and means that when function overloads are defined, erroneous template instantiations do not cause a compilation error, but are discarded from the candidate list for the most appropriate overload .

Now let's see how this code will behave. The Test class defines two types, True and False . Their important property is that sizeof(False) != sizeof(True<T>) .

We also have two detect methods at our disposal. The first takes a pointer to T , the second takes an arbitrary number of arguments (ellipsis). Ellipsis is omnivorous, and at the same time has the lowest priority when choosing overload .
When you call detect(static_cast<T*>(0) compiler tries to find a suitable overload. The template version of detect has a higher priority. But if the type T does not have a void foo() method, then the template instantiation will fail. But as we remember substitution failure is not an error The compiler will give preference to ellipsis.

We can find out which version of detect was chosen by the type of the return value. And we can distinguish types by their size.

Note that there is no detect method implementation in the code. It uses the fact that sizeof does not evaluate the value of the expression, it immediately extracts the type . Thus, the detect methods will never be called.

As a result, the exists variable will be true if the type T has a void foo() method.

All code is executed at compile time and does not give any overhead.

Also, this code was compiled by the online compiler in compatibility mode with C ++ 98

UPD: In the comments, I was hinted that this does not solve your problem. To some extent, this is so. The resulting value cannot be simply used in an if , as this will lead to a compilation error. We need an analogue of if that would work at compile time. Template specialization can act as such an if :

template<class T, bool b = Test<T>::exists>
struct Foo;

template<class T>
struct Foo<T, true>{
    static void foo(const T &t){
        t.foo();
    }
};

template<class T>
struct Foo<T, false>{
    static void foo(const T &t){
        std::cout << "foo" << std::endl;
    }
};

The Foo structure has two specializations. The first for the case Test<T>::exists == true , the second for false . Well, and a little cherry on top for convenience, which does not add anything new except for automatic inference of the type of arguments:

template<class T>
void foo(const T &t){
    Foo<T>::foo(t);
}

Now main will look like this:

int main(){
    A a;
    B b;

    foo(a); //A::foo
    foo(b); //foo
}

Complete example

Scroll to Top