4
votes

La spécialisation des modèles avec enable_if échoue dans Clang, fonctionne avec GCC

J'essaie de supprimer une fonction membre en fonction du type de modèle. Le problème est de faire correspondre une spécialisation de modèle ultérieure à la signature de type de ma fonction dans un cas où elle n'est pas supprimée.

J'ai essayé le code suivant, qui compile avec GCC (9.0.1) mais donne une erreur dans Clang ( 9.0.0). Je pense qu'il ne parvient pas non plus à créer le code dans MSVC ++.

prog.cc:16:88: error: out-of-line definition of 'my_fun' does not match any declaration in 'my_type<double>'
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
                                                                                       ^~~~~~
1 error generated.

L'erreur avec Clang est

#include <type_traits>
#include <iostream>

template <typename T>
struct my_type {
    template <typename Q = T>
    std::enable_if_t<!std::is_same<bool, Q>::value, my_type<T>> my_fun(const my_type<T>& v) {
        std::cout << "Base";
        return v;
    }
};

template <>
template <typename Q> 
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>> my_type<double>::my_fun(const my_type<double>& v) {
    std::cout << "Specialized";
    return v;
}


int main()
{
    my_type<double> aa, bb;
    aa.my_fun(bb);
}

Je voudrais savoir comment faire fonctionner le code, et aussi pourquoi les résultats ne sont pas cohérents entre tous les principaux compilateurs.


8 commentaires

Quelle est l'erreur?


@interjay j'ai ajouté l'erreur à la description


Le problème infâme ... je l'ai vu de temps en temps :(


Hors sujet: std :: is_same :: value serait évalué à false de toute façon, il n'est donc pas nécessaire d'avoir le std :: enable_if autour de tout plus...


Il s'agit probablement d'un bogue gcc. MSVS échoue également à se compiler. Cela semble vraiment bizarre d'avoir Q dans la spécialisation mais de ne pas l'utiliser. Y a-t-il une raison pour laquelle vous utilisez ce modèle principal?


@Aconcagua: vous avez raison. Au départ, j'avais juste "my_type " comme type de retour (puisque c'est ce à quoi il se résume pour T = double), mais j'ai fini par obtenir le type d'erreur "mismatch". C'est à ce moment-là que j'ai pensé que je devais aussi inclure enable_if dans la spécialisation


template <> template <> std :: enable_if_t :: value, my_type > ... le fera compiler sur clang et gcc. Échoue toujours avec MSVS.


Pourquoi ne pas spécialiser my_type pour bool ?


3 Réponses :


2
votes

Je ne sais pas comment faire fonctionner cela avec la spécialisation. Mais je sais comment contourner complètement le problème:

template <typename U>
my_type my_fun_impl(const my_type& v, tag<U>) {
    std::cout << "Base";
    return v;
}

my_type my_fun_impl(const my_type& v, tag<double>) {
    std::cout << "Specialized";
    return v;
}

avec:

template <typename> struct tag { };

template <typename Q = T>
std::enable_if_t<!std::is_same_v<bool, Q>, my_type<T>> my_fun(const my_type<T>& v) {
    return my_fun_impl(v, tag<Q>{});
}

Si vous vouliez une spécialisation pour donner aux utilisateurs possibilité d'ajouter des implémentations spécialisées, vous pouvez faire de my_fun_impl une fonction gratuite au lieu d'une fonction membre. Si le but était simplement de se spécialiser pour certains types, vous pouvez en faire des fonctions membres privées.


0 commentaires

3
votes

Dans les deux cas: mon_type est spécialisé pour double . Ensuite, comparez la version non spécialisée de my_fun

template < >
template < >
//        ^
std::enable_if_t<!std::is_same<bool, double>::value, my_type<double>>
my_type<double>::my_fun<double>(const my_type<double>& v)
//                        ^

avec la version entièrement spécialisée my_fun:

template < >
template <typename Q> 
std::enable_if_t<!std::is_same_v<bool, Q>::value, my_type<double>>
//                                     ^ (!)
my_type<double>::my_fun(const my_type<double>& v)


2 commentaires

Reg. votre dernier commentaire. La raison pour laquelle j'ai besoin de faire de my_fun un modèle de fonction est que vous obtiendrez autrement des erreurs de compilation lors de la création d'un objet de type my_type (il se plaindra de enable_if <** false **, ...> n'ayant pas de type nommé "type"). Mais si vous en faites une fonction de modèle, cette erreur disparaîtra. (Je suppose que SFINAE?)


@DDaniel Peut-être une bonne idée pour éviter les 'cross-instanciations' en ajoutant une deuxième vérification: && std :: is_same_v ...



1
votes

Vous ne pouvez pas utiliser enable_if ici pour supprimer une fonction membre en fonction du paramètre de modèle de la classe, c'est-à-dire T (mais uniquement en fonction de le paramètre de modèle de la fonction, c'est-à-dire Q .

Votre code est erroné, comme clang le souligne à juste titre. Je ne sais pas pourquoi gcc l'accepte et comment il peut détecter ce que Q est dans votre 'spécialisation' (je pense que votre code compilé avec gcc a déclaré "Base" - correct? De plus, comme il n'y a pas d'héritage, il n'est pas clair pourquoi vous utilisez "Base" . )

Sans type de balise, vous pouvez faire ce qui suit.

template <typename T>
struct my_type {
  private:
    template<bool Standard>
    std::enable_if_t<Base, my_type> my_fun_impl(const my_type& v)
    {
        std::cout << "Standard";
        return v;
    }
    template<bool Standard>
    std::enable_if_t<!Standard, my_type> my_fun_impl(const my_type& v)
    {
        std::cout << "Specialised";
        return v;
    }
  public:
    my_type my_fun(const my_type& v)
    {
        return my_fun_impl<is_standard<T>::value>(v);
    }
};

pour tout is_standard que vous voulez.


0 commentaires