J'essaie d'implémenter une liste en utilisant Méta-programmation de modèles mais sans succès de compilation le code suivant:
In instantiation of âstruct List<Int<3> >â: error: wrong number of template arguments (0, should be at least 1)
Avec une erreur:
#include <iostream> template<int Value> struct Int { static constexpr int value = Value; }; template<typename H, typename ...TT> struct List { typedef H head; typedef List<TT...> next; // <-- Too few template arguments for class template 'List' constexpr static int size = sizeof...(TT) + 1; }; int main() { typedef List<Int<1>, Int<2>, Int<3>> list1; static_assert(list1::head::value == 1, "Failed"); // = Int<1> static_assert(list1::size == 3, "Failed"); // = 3 typedef typename list1::next list1Tail; // = List<Int<2>, Int<3>> static_assert(list1Tail::head::value == 2, "Failed"); static_assert(list1Tail::size == 2, "Failed"); // = 2 typedef typename list1Tail::next list2Tail; // = List<Int<3>> <<---error: wrong number of template arguments (0, should be at least 1) static_assert(list2Tail::head::value == 3, "Failed"); static_assert(list2Tail::size == 1, "Failed"); std::cout << "Passed" << std::endl; }
Je comprends que dans mon cas, List
doit gérer deux types H
et ... TT
, mais:
List
ne suffit pas? 4 Réponses :
À la dernière étape, List
instanciera la spécialisation List
qui n'est pas définie dans votre code. Vous devez également écrire une spécialisation "terminale":
template<typename H> struct List<H> { typedef H head; typedef void next; constexpr static int size = 1; };
@DennisVash Cela dépend vraiment des cas d'utilisation de ce modèle. Je viens de copier le corps du modèle principal, mais toujours avoir à la fois les nœuds head
et next
semble raisonnable.
Fournissez une spécialisation pour un élément afin de ne pas essayer d'instancier une List
avec un pack vide.
template <class H> struct List<H> { typedef H head; constexpr static int size = 1; };
Vous avez besoin d'une spécialisation pour une liste vide ou une liste avec un seul élément. Une possibilité est de déclarer d'abord un modèle entièrement variadique, puis de créer deux spécialisations:
template <typename...> struct List; template <typename H, typename... TT> struct List<H, TT...> { using head = H; using next = List<TT... >; constexpr static int size = sizeof... (TT) + 1; }; template <> struct List<> { constexpr static int size = 0; };
De cette façon, vous pouvez avoir une liste vide List
que vous ne pouvez pas avoir avec votre version actuelle.
Variante de la solution de Holt: au lieu d'une seconde spécialisation, le cas fondamental de la récursivité peut être le modèle principal
template <typename...> struct List { constexpr static int size = 0; }; template<typename H, typename ...TT> struct List<H, TT...> { using head = H; using next = typedef List<TT...>; constexpr static int size = sizeof...(TT) + 1; };
Malheureusement, il est moins lisible.
Pensez à ce qui se passe lorsque
TT
est vide, vous avezList
mais vous avez besoin d'au moinsH
pour définir au moins. Vous avez besoin d'une spécialisation pour la fin de la récursivité.