31

Why this does not compile with gcc48 and clang32?

#include <type_traits>

template <int N> 
struct S {

    template<class T> 
    typename std::enable_if<N==1, int>::type
    f(T t) {return 1;};

    template<class T> 
    typename std::enable_if<N!=1, int>::type
    f(T t) {return 2;};
};

int main() {
    S<1> s1;
    return s1.f(99);
}

GCC error:

/home/lvv/p/sto/test/t.cc:12:2: error: no type named ‘type’ in ‘struct enable_if<false, int>’
  f(T t) {return 2;};
  ^

CLANG error:

/home/lvv/p/sto/test/t.cc:11:26: error: no type named 'type' in 'std::enable_if<false, int>'; 'enable_if' cannot be used to
      disable this declaration
        typename std::enable_if<N!=1, int>::type
                                ^~~~
/home/lvv/p/sto/test/t.cc:16:7: note: in instantiation of template class 'S<1>' requested here
        S<1> s1;
             ^

EDIT - SOLUTION

I've accepted answer from Charles Salvia, but for practical reasons I was not able to use workaround that was proposed (specialize on N). I found other workaround which works for me. Make enable_if depend on T:

typename std::enable_if<(sizeof(T),N==1), int>::type
Leonid Volnitsky
  • 8,854
  • 5
  • 38
  • 53
  • 1
    Are you sure this is going to work anyways? You cannot overload methods just by their return type (unless `enable_if` is implemented as a language construct rather than by, as I am currently assuming, a simple templated class). – zneak Dec 20 '12 at 02:41
  • 1
    Why is the workaround of making enable_if depend on T necessary, exactly? – Justin L. Oct 14 '13 at 19:03
  • The solution does not work for me! I got `error: no matching function for call ...` – Li Dong Mar 20 '14 at 14:52
  • @LiDong - My Solution? This is why you downvoted the question? I was asking a question here. For answers look below. – Leonid Volnitsky Mar 22 '14 at 20:51
  • @LeonidVolnitsky The `sizeof(T)` does not work in my case. Sorry about the downvote. I was just frustrated then. I used default template argument in C++11. – Li Dong Mar 23 '14 at 00:22

5 Answers5

22

Because you use enable_if without using the template parameter T in your function templates. If you want to specialize for when the struct S has a certain template parameter value N, you'll need to use class template specialization.

template <int N, class Enable = void> 
struct S {  };

template <int N>
struct S<N, typename std::enable_if<N == 1>::type>
{
  ....
};
Charles Salvia
  • 52,325
  • 13
  • 128
  • 140
10

Well, I am not sure what you wanted to do, but maybe this code will help:

#include <iostream>

template <int N>
struct S {

    template<class T=int>
    typename std::enable_if<N==1, T>::type
    f(T t) {return 1;}

    template<class T=int>
    typename std::enable_if<N!=1, T>::type
    f(T t) {return 2;}
};

int main()
{
    S<1> s1;
    S<2> s2;
    std::cout << s1.f(99) << " " << std::endl << s2.f(5);
}

This prints 1 and 2.

Community
  • 1
  • 1
foxfireee
  • 180
  • 1
  • 7
9

To get std::enable_if to work like this, you are relying on SFINAE. Unfortunately, at the point where you declare

S<1> s1;

it will instantiate all of S<1>'s member declarations. SFINAE will only come into play at this point if S<1> were an ill-formed construct. It is not. Unfortunately, it contains a function which is invalid, thus the instantiation of S<> is invalid.

For things like this, I might defer to a seperate template struct:

template <bool B>
struct f_functor {
    template <typename T>
    static int f(T t) { return 1; }
};

template <>
struct f_functor<false> {
    template <typename T>
    static int f(T t) { return 2; }
};

template <int N> 
struct S {

    template<class T> 
    typename int f(T t) { return f_functor<N==1>::f(t); }
};
Anthony
  • 12,177
  • 9
  • 69
  • 105
  • 1
    [This question](http://stackoverflow.com/questions/6972368/stdenable-if-to-conditionally-compile-a-member-function) has some more information. – Anthony Dec 20 '12 at 03:02
9

Use a default boolean template parameter, like this:

template <int N> 
struct S {

    template<class T, bool EnableBool=true> 
    typename std::enable_if<N==1 && EnableBool, int>::type
    f(T t) {return 1;};

    template<class T, bool EnableBool=true> 
    typename std::enable_if<N!=1 && EnableBool, int>::type
    f(T t) {return 2;};
};
Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
2

For this case you could think about not using enable_if at all. It is posible to simply specialise f:

template <int N> 
struct S {
    template<class T> int f(T t);
};

template<int N>
template<class T>
int S<N>::f(T t) { return 2; }

template<>
template<class T>
int S<1>::f(T t) { return 1; }

int main() {
    S<1> s1;
    return s1.f(99);
}
Jan Herrmann
  • 2,717
  • 17
  • 21