C++ Itération sur un paquet de paramètres


Exemple

Souvent, nous devons effectuer une opération sur chaque élément d'un pack de paramètres de modèle variadic. Il existe plusieurs façons de procéder, et les solutions sont plus faciles à lire et à écrire avec C ++ 17. Supposons que nous voulions simplement imprimer chaque élément d'un pack. La solution la plus simple consiste à faire appel à:

C ++ 11
void print_all(std::ostream& os) {
    // base case
}

template <class T, class... Ts>
void print_all(std::ostream& os, T const& first, Ts const&... rest) {
    os << first;
    
    print_all(os, rest...);
}

Nous pourrions utiliser l'astuce d'expansion pour effectuer tout le streaming en une seule fonction. Cela présente l'avantage de ne pas nécessiter une seconde surcharge, mais présente l'inconvénient d'une lisibilité inférieure aux étoiles:

C ++ 11
template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
    using expander = int[];
    (void)expander{0,
        (void(os << args), 0)...
    };
}

Pour une explication de son fonctionnement, voir l'excellente réponse de TC .

C ++ 17

Avec C ++ 17, nous avons deux nouveaux outils puissants dans notre arsenal pour résoudre ce problème. Le premier est une expression de pli:

template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
    ((os << args), ...);
}

Et la seconde est if constexpr , ce qui nous permet d’écrire notre solution récursive originale en une seule fonction:

template <class T, class... Ts>
void print_all(std::ostream& os, T const& first, Ts const&... rest) {
    os << first;

    if constexpr (sizeof...(rest) > 0) {        
        // this line will only be instantiated if there are further
        // arguments. if rest... is empty, there will be no call to
        // print_all(os). 
        print_all(os, rest...);
    }
}