Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why compile error with enable_if

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
like image 877
Leonid Volnitsky Avatar asked Dec 20 '12 02:12

Leonid Volnitsky


3 Answers

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> {   .... }; 
like image 186
Charles Salvia Avatar answered Sep 28 '22 19:09

Charles Salvia


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.

like image 30
foxfireee Avatar answered Sep 28 '22 20:09

foxfireee


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); }
};
like image 34
Anthony Avatar answered Sep 28 '22 18:09

Anthony