Pourquoi compiler l'erreur avec enable if

Pourquoi cela ne compile pas avec gcc48 et 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);
}

Erreur GCC:

/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 erreur:

/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;
             ^

MODIFIER-SOLUTION

J'ai accepté la réponse de Charles Salvia, mais pour des raisons pratiques, je n'ai pas pu utiliser la solution de contournement proposée (se spécialiser sur N). J'ai trouvé une autre solution de contournement qui fonctionne pour moi. Faites en sorte que enable_if dépende de T:

typename std::enable_if<(sizeof(T),N==1), int>::type
25
demandé sur Leonid Volnitsky 2012-12-20 06:34:33

5 réponses

Parce que vous utilisez enable_if sans utiliser le paramètre template T dans vos modèles de fonction. Si vous souhaitez vous spécialiser lorsque la structure S a une certaine valeur de paramètre de modèle N, vous devrez utiliser la spécialisation de modèle de classe.

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

template <int N>
struct S<N, typename std::enable_if<N == 1>::type>
{
  ....
};
16
répondu Charles Salvia 2012-12-20 02:41:14

Eh bien, je ne suis pas sûr de ce que vous vouliez faire, mais peut-être que ce code aidera:

#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);
}

Cela imprime 1 et 2.

9
répondu foxfireee 2015-11-25 13:42:39

Utilisez un paramètre de modèle booléen par défaut, comme ceci:

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;};
};
8
répondu Paul Fultz II 2014-08-16 20:46:52

Pour que std::enable_if fonctionne comme ça, vous comptez sur SFINAE. Malheureusement, au moment où vous déclarez

S<1> s1;

Il instanciera toutes les déclarations des membres de S<1>. SFINAE n'entrera en jeu à ce stade que si S<1> était une construction mal formée. Il n'est pas. Malheureusement, il contient une fonction qui n'est pas valide, donc l'instanciation de S<> n'est pas valide.

Pour des choses comme celle-ci, je pourrais m'en remettre à une structure de modèle distincte:

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); }
};
5
répondu Anthony 2012-12-20 03:09:08

Pour ce cas, vous pourriez penser à ne pas utiliser enable_if du tout. Il est possible de simplement se spécialiser 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);
}
1
répondu Jan Herrmann 2012-12-20 08:50:16