c++templatessfinaemember-pointers

What does template<typename T, T> mean?


I was reading this prehistoric metaprogam example to detect whether a class supports member find. (or any other member).

template<typename T>
class DetectFind
{
    struct Fallback { int find; }; 
    struct Derived : T, Fallback { };

    template<typename U, U> struct Check;

    typedef char Yes[1];  
    typedef char No[2]; 

    template<typename U>
    static No& func(Check<int Fallback::*, &U::find>*);

    template<typename U>
    static Yes& func(...);

public:
    typedef DetectFind type;
    enum { value = sizeof(func<Derived>(0)) == sizeof(Yes) };
};

int main()
{
    std::cout << DetectFind<std::vector<int> >::value << std::endl;
    std::cout<< DetectFind<std::set<int> >::value << std::endl;
}

Intuitively I do understand the aim behind this, but if someone would ask me to write same thing from scratch after 10 days, I maybe will fail this.
The reason for that is that I do not fully understand the syntactical and language stretch that is used here.
Can someone please explain what the following syntaxes mean?

  1. Check<int Fallback::*, &U::find>* (I know its trying to benefit from SFIANE here, but how is this detecting the existence of find, I beleive this is linked to the second question as well)
  2. template<typename U, U> struct Check;

The program outputs 0 1 as predicted;


Solution

  • First, let us consider the struct Derived. Since it derives from Fallback it certainly contains a int field find, and possibly a member function find, whose existence is what you want to check.

    As noted in the answer above, in the declaration of the struct Check, the first template parameter is as type, and the second is a non-type parameter, of the type given by the first parameter.

    Given that, let us examine the two overloads of func. The first overload takes a pointer to a Check struct, whose first template parameters is a type equal to pointer-to-int member of Fallback (int Fallback::*). The second template parameter is then interpreted as a pointer-to-int member with value is &U::find. Given U = Derived, if T contains a find member function, this second parameter of Check is ambiguous, as it could also refer to the int find inherited by Fallback. By SFINAE, this overload of func will be then discarded.

    The second func overload is always well defined. But if the first overload is not discarded, the second one is less specialized, so the the compiler will choose the first overload.

    In summary: if U in template <typename U> func contains a member function find, the compiler chooses the second overload of func. If the member function find is absent, the compiler chooses the first overload.

    Finally, the value of DetectFind is determined by the size of the returning type of the chosen func, which depending on the overload is a char array of size 1 or 2. From there, you get which overload of func is chosen, and from the discussion above, whether T has a member function find or not.