web-dev-qa-db-fra.com

Pourquoi compiler une 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;};
  ^

Erreur CLANG:

/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 qui a été proposée (se spécialiser sur N). J'ai trouvé une autre solution de contournement qui fonctionne pour moi. Faire enable_if dépend de T:

typename std::enable_if<(sizeof(T),N==1), int>::type
28
Leonid Volnitsky

Parce que vous utilisez enable_if sans utiliser le paramètre de modèle 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>
{
  ....
};
21
Charles Salvia

Eh bien, je ne suis pas sûr de ce que vous vouliez faire, mais peut-être que ce code vous 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.

10
foxfireee

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
Paul Fultz II

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

S<1> s1;

il instanciera toutes les déclarations de membres de S<1>. SFINAE n'entrera en jeu à ce stade que si S<1> Était une construction mal formée. Ce 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 ça, 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); }
};
6
Anthony

Dans ce cas, vous pourriez penser à ne pas utiliser enable_if du tout. Il est possible de simplement 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
Jan Herrmann