Substitution failure is not an error, part I
Posted by: rmn 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.
Advertisement
Like this:
Be the first to like this post.
11/09/2009 at 17:25
Just to make your code clearer, rename B::X (uppercase x) to B::y.
11/09/2009 at 17:39
Agreed.
B now has a member named y instead of X, to prevent possible mixups.
Thanks.