I am trying to get a simple example to work to understand how to use std::enable_if
. After I read this answer, I thought it shouldn't be too hard to come up with a simple example. I want to use std::enable_if
to choose between two member-functions and allow only one of them to be used.
Unfortunately, the following doesn't compile with gcc 4.7 and after hours and hours of trying I am asking you guys what my mistake is.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc reports the following problems:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Why doesn't g++ delete the wrong instantiation for the second member function? According to the standard, std::enable_if< bool, T = void >::type
only exists when the boolean template parameter is true. But why doesn't g++ consider this as SFINAE? I think that the overloading error message comes from the problem that g++ doesn't delete the second member function and believes that this should be an overload.
The boolean needs to depend on the template parameter being deduced. So an easy way to fix is to use a default boolean parameter:
However, this won't work if you want to overload the member function. Instead, its best to use
TICK_MEMBER_REQUIRES
from the Tick library:You can also implement your own member requires macro like this(just in case you don't want to use another library):
I made this short example which also works.
Comment if you want me to elaborate. I think the code is more or less self-explanatory, but then again I made it so I might be wrong :)
You can see it in action here.
From this post:
But one can do something like this:
SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. There is no such substitution.
That's because when the class template is instantiated (which happens when you create an object of type
Y<int>
among other cases), it instantiates all its member declarations (not necessarily their definitions/bodies!). Among them are also its member templates. Note thatT
is known then, and!std::is_same< T, int >::value
yields false. So it will create a classY<int>
which containsThe
std::enable_if<false>::type
accesses a non-existing type, so that declaration is ill-formed. And thus your program is invalid.You need to make the member templates'
enable_if
depend on a parameter of the member template itself. Then the declarations are valid, because the whole type is still dependent. When you try to call one of them, argument deduction for their template arguments happen and SFINAE happens as expected. See this question and the corresponding answer on how to do that.For those late-comers that are looking for a solution that "just works":
Compile with:
Running gives:
One way to solve this problem, specialization of member functions is to put the specialization into another class, then inherit from that class. You may have to change the order of inheritence to get access to all of the other underlying data but this technique does work.
The disadvantage of this technique is that if you need to test a lot of different things for different member functions you'll have to make a class for each one, and chain it in the inheritence tree. This is true for accessing common data members.
Ex: