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.