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?
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)template<typename U, U> struct Check;
The program outputs 0 1 as predicted;
First, let us consider the struct
Derived. Since it derives fromFallbackit certainly contains a int fieldfind, and possibly a member functionfind, 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 aCheckstruct, whose first template parameters is a type equal to pointer-to-int member ofFallback(int Fallback::*). The second template parameter is then interpreted as a pointer-to-int member with value is&U::find. GivenU = Derived, ifTcontains afindmember function, this second parameter ofCheckis ambiguous, as it could also refer to theint findinherited byFallback. By SFINAE, this overload offuncwill be then discarded.The second
funcoverload 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
Uintemplate <typename U> funccontains a member functionfind, the compiler chooses the second overload offunc. If the member functionfindis absent, the compiler chooses the first overload.Finally, the value of
DetectFindis determined by the size of the returning type of the chosenfunc, which depending on the overload is a char array of size 1 or 2. From there, you get which overload offuncis chosen, and from the discussion above, whetherThas a member functionfindor not.