G ++ and clang ++ different behavior on SFINAE and SFINAE crashes

A few questions for C ++ 11 experts.

I'm fighting SFINAE and I came across a strange case where g ++ (4.9.2) and clang ++ (3.5.0) behave differently.

I prepared the following code example. Sorry, but I can not make it much more concise.

#include <string> #include <iostream> #include <typeinfo> #include <type_traits> template <typename X> class foo { private: template <typename R> using enableIfIsInt = typename std::enable_if<std::is_same<X, int>::value, R>::type; public: foo () { } template <typename R = void> enableIfIsInt<R> bar () { std::cout << "bar: is int\n"; } void bar () { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" << typeid(enableIfIsInt<void>).name() << "}\n"; } }; int main () { foo<long> fl; foo<int> fi; fl.bar(); fi.bar(); return 0; } 

My idea was to create a foo<X> template that (via SFINAE) could define a method in one way or another, depending on the argument of X template.

The program compiles well with g ++ 4.9.2, but clang ++ 3.5.0 gives the following error

 test.cpp:13:36: error: no type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration = typename std::enable_if<std::is_same<X, int>::value, R>::type; ^~~~~~~~~~~~~~~~~~~~~~~~~~~ test.cpp:26:23: note: in instantiation of template type alias 'enableIfIsInt' requested here << typeid(enableIfIsInt<void>).name() << "}\n"; ^ test.cpp:36:7: note: in instantiation of member function 'foo<long>::bar' requested here fl.bar(); ^ 1 error generated. 

I suppose this is the correct clang ++, but my first question is for C ++ 11 experts: who is right? g ++ or clang ++?

About the release of a program released by g ++, follows

 bar: isn't int; is [i]{v} 

therefore g ++ seems to ignore the fl.bar(); statement fl.bar(); .

Now a small change: I am changing the second version of foo<X>::bar() this way

  void bar () { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; } 

removing std::enable_if inside the abomination function. Now both g ++ and clang ++ compile without problems, and the output for both compiled versions of the program is

 bar: isn't int; is [l] bar: isn't int; is [i] 

So my second question is: what am I doing wrong? Why in case of int I do not get the version of "is int" foo<X>::bar() ?

Be patient with me if I do stupid things: I'm trying to learn C ++ 11.

And sorry for my bad english.

+6
source share
1 answer

The clang error does not occur due to replacement failure. This comes from here:

  void bar () { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" << typeid(enableIfIsInt<void>).name() << "}\n"; // <== } 

enableIfIsInt<void> not in the immediate context that the hard failure for X not int . You simply cannot use this expression in this context.

Once you remove this, it is not always the bar() pattern that is called. This is because both functions are equivalent, and not patterns are preferable to patterns when resolving overloads.

So, the real solution is to use tag dispatching:

 void bar() { bar(std::is_same<X, int>{}); } void bar(std::true_type ) { std::cout << "bar: is int\n"; } void bar(std::false_type ) { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; } 

with which both compilers gladly come out:

 bar: isn't int; is [l] bar: is int 
+6
source

All Articles