3
votes

Métaprogrammation de modèle: nombre incorrect d'arguments de modèle dans le pack de paramètres

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:

  1. Pourquoi appeler List ne suffit pas?
  2. Comment dois-je résoudre ce problème?
  3. Quelle est la meilleure approche dans ce cas pour compiler le code?


1 commentaires

Pensez à ce qui se passe lorsque TT est vide, vous avez List mais vous avez besoin d'au moins H pour définir au moins. Vous avez besoin d'une spécialisation pour la fin de la récursivité.


4 Réponses :


2
votes

À 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;
};

compilateur en ligne a>


1 commentaires

@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.



1
votes

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;
};


0 commentaires

2
votes

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.


0 commentaires

0
votes

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.


0 commentaires