Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this SFINAE C++ syntax work?

I have just begun dabbling with SFINAE and I am having trouble understanding the syntax behind the most often-used example which appears in various forms, but the idea is to check whether a particular type contains a given member. This particular one is from Wikipedia:

template <typename T> struct has_typedef_foobar 
{
    typedef char yes[1];
    typedef char no[2];

    template <typename C> static yes& test(typename C::foobar*);
    template <typename> static no& test(...);

    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

There are a couple of things I don't get about this:

  1. What is the argument type of the test() overload that returns "yes"? Is it a pointer? Why the typename keyword used as part of the argument? I've seen it used also for testing if a class has a member of a given type, not just a typedef, and the syntax remains unchanged.
  2. Sometimes I've seen examples that use test(int C::*). This is even more strange, no idea what member of C are we referencing. If it was a real function with a body, instantiated for a real type, and the argument was named, what would it point to and how would you use it?
    template <typename T> func(int T::*arg)
    {
        *arg = 1;
    }
    
    struct Foo
    {
        int x;
    } foo;
    
    func<Foo>(&foo::x); // something like this?
    func(&foo::x); // or maybe even like this?
    
  3. Is template <typename> with no symbol allowed if it's not being used in the second overload? How is it even a template function then?
  4. Bonus: Could it be made to check for existence of more than one member at a time?
like image 207
neuviemeporte Avatar asked Nov 21 '25 11:11

neuviemeporte


1 Answers

Most of these questions have nothing to do with SFINAE:

  1. When a dependent name should be considered a type, it needs to be preceded by typename. Since C is a template parameter to test(), clearly C::foobar is a dependent name. Even though function declarations require a type in front of each argument, the language requires the use of typename to turn the dependent name C::foobar into a type. With that, typename C::foobar is just a type and applying a the type constructor * to it, produces the corresponding pointer type.
  2. int C::* is an unnamed pointer to data member of type int.
  3. Names which are not used can always be left out. This applies to function arguments as well as to the template parameters, i.e., yes, the name after template can be omitted if it isn't used. Most of the time it is used in some form, though, in which case it is, obviously, required.
  4. I'd think you can write a test which tests for the presence of multiple aspects but I wouldn't to it: SFINAE is unreadable enough as it is. I'd rather explicit combine the different property tests with normal logical operators.
like image 140
Dietmar Kühl Avatar answered Nov 24 '25 01:11

Dietmar Kühl