2 votes

Spécialisation et sélection des modèles dans les classes de modèles variadiques

J'ai une classe modèle variadique avec quelques spécialisations de modèle. J'aimerais, pour certaines spécialisations, les activer de manière conditionnelle (avec le paramètre std::enable_if J'ai donc ajouté un paramètre de modèle supplémentaire inutilisé).

Voici un exemple de code :

#include <tuple>
#include <iostream>

template<int v,typename, typename...>
struct A;

template<int v, typename...Ts, typename...Us>
struct A<v,void,std::tuple<Ts...>, Us...> {
   void print() {
    std::cout << "A: Tuple selected" << std::endl;
   }
};

template<int v, typename T, typename...Us>
struct A<v,void,T, Us...> {
   void print() {
    std::cout << "A: one element selected" << std::endl;
   }
};

template<int v,typename, typename...>
struct B;

template<int v, typename...Ts, typename...Us>
struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
   void print() {
    std::cout << "B: Tuple selected" << std::endl;
   }
};

template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==12>,T, Us...> {
   void print() {
    std::cout << "B: one element selected" << std::endl;
   }
};

template<int v, typename...>
struct C;

template<int v, typename...Ts, typename...Us>
struct C<v,std::enable_if_t<v!=11,std::tuple<Ts...>>, Us...> {
   void print() {
    std::cout << "C: Tuple selected" << std::endl;
   }
};

template<int v, typename T, typename...Us>
struct C<v, T, Us...> {
   void print() {
    std::cout << "C: one element selected" << std::endl;
   }
};

int main()
{
   struct A<12,void,std::tuple<int>> a;
   a.print();
   struct B<12,void,std::tuple<int>> b;
   b.print();
   struct C<12,std::tuple<int>> c;
   c.print();
   return 0;
}

Instanciation pour a œuvre (et le tuple la spécialisation est effectivement choisie).

Instanciation pour b échoue en raison d'une instatiation de modèle ambiguë. Avec gcc :

$ g++ -std=gnu++17 -Wall -Wextra toto5.cpp
toto5.cpp: In function ‘int main()’:
toto5.cpp:61:38: error: ambiguous template instantiation for ‘struct B<12, void, std::tuple<int> >’
   61 |    struct B<12,void,std::tuple<int>> b;
      |                                      ^
toto5.cpp:25:8: note: candidates are: ‘template<int v, class ... Ts, class ... Us> struct B<v, typename std::enable_if<(v != 11), void>::type, std::tuple<_UTypes ...>, Us ...> [with int v = 12; Ts = {int}; Us = {}]’
   25 | struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:32:8: note:                 ‘template<int v, class T, class ... Us> struct B<v, typename std::enable_if<(v == 12), void>::type, T, Us ...> [with int v = 12; T = std::tuple<int>; Us = {}]’
   32 | struct B<v,std::enable_if_t<v==12>,T, Us...> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:61:38: error: aggregate ‘B<12, void, std::tuple<int> > b’ has incomplete type and cannot be defined
   61 |    struct B<12,void,std::tuple<int>> b;
      |                                      ^

Pour C, l'erreur se produit lors de la lecture du modèle lui-même (pas besoin d'instancier) :

$ g++ -std=gnu++17 -Wall -Wextra toto5.cpp
toto5.cpp:42:8: error: template parameters not deducible in partial specialization:
   42 | struct C<v,std::enable_if_t<v!=11,std::tuple<Ts...>>, Us...> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:42:8: note:         ‘Ts’

Que peut-on faire ?

Remarque : je sais que la condition dans std::enable_if ne sont pas utiles, mais il ne s'agit ici que d'un exemple.

Une façon de procéder serait d'utiliser le B mais en changeant le std::enable_if pour désactiver la deuxième spécialisation du modèle, mais comment dire "pas un tuple d'aucune sorte" ? std::is_same ne semble pas utile ici.

clang++ me donne les mêmes types d'erreurs dans tous ces cas.

2voto

Sedenion Points 625

Pour la spécialisation B vous devez vous assurer que les conditions pour std::enable_if sont orthogonales. Dans votre version, si vous fournissez v=12 les deux conditions v!=11 y v==12 rendement true ce qui signifie que les deux versions sont activées. C'est la raison pour laquelle vous obtenez l'erreur d'instanciation ambiguë. Ce qui suit compile bien ( https://godbolt.org/z/csaTWMd9v ) :

#include <tuple>
#include <iostream>

template<int v,typename, typename...>
struct B;

template<int v, typename...Ts, typename...Us>
struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
   void print() {
    std::cout << "B: Tuple selected" << std::endl;
   }
};

template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==11>,T, Us...> {
   void print() {
    std::cout << "B: one element selected" << std::endl;
   }
};

int main()
{
   struct B<12,void,std::tuple<int>> b;
   b.print();
}

Mise à jour : Comme demandé dans les commentaires, un moyen de vérifier si un certain paramètre de modèle est un tuple de n'importe quel type peut être fait comme suit (comparez également e.g. ce poste ). Est-ce plus proche de ce que vous voulez obtenir ? ( https://godbolt.org/z/dq85fM7KK )

#include <tuple>
#include <iostream>

template <typename>
struct is_tuple : std::false_type
{ };

template <typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type
{ };

template<int v,typename, typename...>
struct B;

template<int v, typename...Ts, typename...Us>
struct B<v, std::enable_if_t<v!=11>, std::tuple<Ts...>, Us...> {
   void print() {
    std::cout << "B: Tuple selected" << std::endl;
   }
};

template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==12 && !is_tuple<T>::value>, T, Us...> {
   void print() {
    std::cout << "B: one element selected" << std::endl;
   }
};

int main()
{
   B<12, void, std::tuple<int>>{}.print(); // Tuple selected
   B<12, void, int>{}.print(); // one element selected
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X