Reputation: 3171
enum class enabler{};
template<typename T>
class X {
template<typename std::enable_if<std::is_class<T>::value,enabler>::type = enabler()>
void func();
void func(int a);
void func(std::string b);
};
I have this class with these 3 overloads for func
. I need the second/third versions to be available for both class/non-class types, and the first version to be available only for class types. when I tried to use enable_if
as above, the class instantiation for non-class types gives compile error.
Upvotes: 3
Views: 3002
Reputation: 109119
For SFINAE to work, the template argument must be deduced. In your case, T
is already known by the time you attempt to instantiate func
, so if the enable_if
condition is false
, instead of SFINAE, you get a hard error.
To fix the error, just add a template parameter whose default value is T
, and use this new parameter in the enable_if
check. Now deduction occurs and SFINAE can kick in for non-class types.
template<typename U = T,
typename std::enable_if<std::is_class<U>::value,enabler>::type = enabler()>
void func();
And you don't really need a dedicated enabler
type either, this works too
template<typename U = T,
typename std::enable_if<std::is_class<U>::value, int>::type* = nullptr>
void func();
Upvotes: 4
Reputation: 50540
You are not enabling or disabling something.
You simply want a compile time error in one specific case.
Because of that you don't require to rely on sfinae, a static_assert
is enough.
As a minimal, working example:
#include<string>
template<typename T>
class X {
public:
void func() {
static_assert(std::is_class<T>::value, "!");
// do whatever you want here
}
void func(int a) {}
void func(std::string b) {}
};
int main() {
X<int> x1;
X<std::string> x2;
x2.func(42);
x2.func();
x1.func(42);
// compilation error
// x1.func();
}
Once a SO user said me: this is not sfinae, this is - substitution failure is always an error - and in this case you should use a static_assert
instead.
He was right, as shown in the above example a static_assert
is easier to write and to understand than sfinae and does its work as well.
Upvotes: 0
Reputation: 36792
I'm not really sure what you're going for with enabler
here, but you can't do what you're trying because the declaration for your member function must be valid since T
is not deduced by func
. To achieve what you want in adding an extra overload, you can use some moderately contrived inheritance.
struct XBaseImpl {
// whatever you want in both versions
void func(int a) { }
void func(std::string b) { }
};
template <typename, bool> struct XBase;
// is_class is true, contains the extra overload you want
template <typename T>
struct XBase<T, true> : XBaseImpl {
static_assert(std::is_class<T>{}, ""); // just to be safe
using XBaseImpl::func;
void func() { } // class-only
};
// is_class is false
template <typename T>
struct XBase<T, false> : XBaseImpl { };
template<typename T>
class X : public XBase<T, std::is_class<T>{}> { };
Upvotes: 1