问题
Here is my somewhat odd code:
template <typename T&>
class A {
public:
void b(typename std::enable_if<!std::is_pointer<T>::value, T>;::type o) {}
void b(typename std::enable_if<std::is_pointer<T>::value, T>;::type o) {}
};
template <typename T>
void b(typename std::enable_if<!std::is_pointer<T>::value, T>::type o) {}
template <typename T>
void b(typename std::enable_if<std::is_pointer<T>::value, T>::type o) {}
If I ifdef
out the method b
and call b<int *>(pi
) where pi
is int *
, everything compiles.
If I ifdef
out the function b
(outside class) and call A<int *> a; a.b(pi)
, I get the following error:
error: no type named 'type' in 'std::__1::enable_if<false, int *>'
Why the inconsistency and how can I fix the problem so that I can use the methods in A?
回答1:
The problem is, that SFINAE only works during overload resolution and only if the function itself is a template. In your method case, the whole class is a template, meaning that there is no substitution of the template parameter (remember: SFINAE == "Substitution Failure Is Not An Error").
At the point of instantiation, the method signatures look like this (nevermind the call to them):
void A<int*>::b(std::enable_if<false, int*>::type o) // error
void A<int*>::b(std::enable_if<true, int*>::type o)
To fix this, make the methods templates too:
template<class T>
class A{
public:
template<class U>
void b(U o, typename std::enable_if<!std::is_pointer<U>::value>::type* = 0){}
// same for the other version
};
On a side note, letting the template argument get deduced is the better way to use SFINAE, so you should modify the free functions to look like this:
template<class T>
void b(T o, typename std::enable_if<!std::is_pointer<T>::value>::type* = 0){}
// same for the other version
In C++11, you can even use the template parameters for SFINAE:
template<class T, EnableIf<std::is_pointer<T>> = {}>
void b(T o);
Utilizing an alias from the blog entry linked from here:
namespace detail{ enum class enabler{}; }
template<class Cond, class T = detail::enabler>
using EnableIf = typename std::enable_if<C::value, T>::type;
回答2:
For the explanation, see Xeo's answer.
For the work-around: just add a dummy template parameter to the method
#include <utility>
#include <type_traits>
template <typename T>
class A {
public:
template <typename U = T>
void b(typename std::enable_if<!std::is_pointer<U>::value, U>::type o);
template <typename U = T>
void b(typename std::enable_if<std::is_pointer<U>::value, U>::type o);
};
template <typename T>
template <typename U>
void A<T>::b(typename std::enable_if<!std::is_pointer<U>::value, U>::type o) {}
template <typename T>
template <typename U>
void A<T>::b(typename std::enable_if<std::is_pointer<U>::value, U>::type o) {}
int main() {
A<int> a;
a.b(0);
}
Live Demo Here.
回答3:
You are not using SFINAE correctly because the compiler can't deduce the argument for enable_if<...>::type
and probably that's why it fails.
Correct declarations of free-standing functions would be:
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, void>::type b(T o);
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, void>::type b(T o);
In this particular case plain function overloading can be used as well:
template <typename T>
void b(T);
template <typename T>
void b(T*);
来源:https://stackoverflow.com/questions/11248173/enable-if-seems-to-work-outside-a-class-but-not-inside