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.
3 Réponses :
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.
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)
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
@DDaniel Peut-être une bonne idée pour éviter les 'cross-instanciations' en ajoutant une deuxième vérification: && std :: is_same_v
...
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.
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 lestd :: 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
pourbool
?