cplusplus.co.il

Substitution failure is not an error, part I

Posted on: 11/09/2009

Explanation on what is SFINAE can be found at wikipedia. I’ve tried to write my own explanation but ended up with the conclusion that it’s best described there, and I wouldn’t want to copy&paste stuff. If you have any question marks floating around your heads, please don’t hesitate to ask.

SFINAE is used alot in meta-programming. For example, you will be able to find many constructs in boost that are making use of this idea.

I’ve come across a pretty nice and complex use of SFINAE on stackoverflow the other day. The code below generates a templated class HasX<T>, whose member HasX<T>::value denotes whether the class T has a member (data or function) called x, or not. Let me introduce the code, and an explanation will follow.

#include <iostream>

template<typename T>
struct HasX {
    struct Fallback {
        int x;
    }; // introduce member name "x"
 
    struct Derived : T, Fallback { };
 
    template<typename C, C>
    struct ChT;    
 
    template<typename C>
    static char (&f(ChT<int Fallback::*, &C::x>*))[1];
 
    template<typename C>
    static char (&f(...))[2];
 
    static bool const value = sizeof(f<Derived>(0)) == 2;
};

struct A {
    int x;
};

struct B {
    int y;
};

int main() {
    std::cout << HasX<A>::value << std::endl; // 1   
    std::cout << HasX<B>::value << std::endl; // 0
}

At first glance it may look like gibberish [Well, at least to gcc3.5 this code looks like gibberish - it dies with "segmentation fault" while attempting to compile. Newer versions handle it just fine, as well as visual studio], but bare with me and I shall explain it.

Just two words on why this is even remotely interesting. Note that this value is available at compile time and is fully constant. Therefore (for example), we are able to write templates with specializations that actually use (or even place compile time asserts on) what we checked for. I will leave it to the readers to think of more ways to exploit this.

Take your time: observe the code, see what you can understand for yourself. The full explanation is posted on part II.

About these ads

6 Responses to "Substitution failure is not an error, part I"

Just to make your code clearer, rename B::X (uppercase x) to B::y.

Agreed.
B now has a member named y instead of X, to prevent possible mixups.
Thanks.

Obviously, it’s a very advanced topic, but I think you could somehow shorten the code to this:

template <typename T>
struct HasX {
    template <class C, C> struct chT;
    template <class C, class U> static char (&hasX(chT<U C::*, &C::x>*))[2];
    template <class C> static char (&hasX( ... ));
    enum { value = sizeof(hasX<T>(0)) == 2 };
};

If C has a member named x the first hasX function will be chosen, otherwise the one with the elipsis will be chosen.

Is there anything wrong with this?

Nice post.

It does not work (verified with vs 2008 for the example given in the post, prints 0 0).

Can you tell why?

Yes, I mistakenly declared a template argument “class U”, just omit it and it will work magically.

If you meant to remove parameter U and then use (pointer-to-member) int instead of it, then it would work – but it would do something different from what we wanted.
The version you suggest would only return true IFF there’s an _integer_ member by the name of x in the given class, not just any member (like the posted version).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 25 other followers

%d bloggers like this: