mkmostafa
mkmostafa

Reputation: 3171

how to use enable_if with overloads

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

Answers (3)

Praetorian
Praetorian

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

skypjack
skypjack

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

Ryan Haining
Ryan Haining

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

Related Questions