web-dev-qa-db-fra.com

Est-il possible d'écrire un modèle de fonction qui retourne si le nombre d'arguments est divisible par N?

J'ai appris sur les modèles variadiques, et avec l'aide de cet excellent article de blog , j'ai réussi à écrire un modèle de fonction even_number_of_args qui retourne si le nombre d'arguments qu'il reçoit est divisible par 2.

#include <iostream>

bool even_number_of_args() {
    return true;
}

template <typename T>
bool even_number_of_args(T _) {
    return false;
}

template<typename T, typename U, typename... Vs>
bool even_number_of_args(T _, U __, Vs... vs) {
  return even_number_of_args(vs...);
}

int main() {
    std::cout << even_number_of_args()                   << std::endl; // true
    std::cout << even_number_of_args(1)                  << std::endl; // false
    std::cout << even_number_of_args(1, "two")           << std::endl; // true
    std::cout << even_number_of_args(1, "two", 3.0)      << std::endl; // false
    std::cout << even_number_of_args(1, "two", 3.0, '4') << std::endl; // true
}

Je me demandais s'il était possible d'écrire un modèle de fonction qui prend, comme argument de modèle, un nombre N et renvoie si le nombre d'arguments qu'il reçoit est un multiple de N. Par exemple, la fonction peut ressembler à ceci:

number_of_args_divisible_by_N<1>(1, "two", 3.0, '4'); // true
number_of_args_divisible_by_N<2>(1, "two", 3.0, '4'); // true
number_of_args_divisible_by_N<3>(1, "two", 3.0, '4'); // false
number_of_args_divisible_by_N<4>(1, "two", 3.0, '4'); // true
46
mwhittaker

Oui, c'est aussi simple que

template<int N, typename... Ts>
constexpr bool number_of_args_divisible_by(Ts&&...)
{
    return sizeof...(Ts) % N == 0;
}

Alternativement, vous pouvez renvoyer un type plus adapté aux métaprogrammations:

template<int N, typename... Ts>
constexpr integral_constant<bool, sizeof...(Ts) % N == 0>
number_of_args_divisible_by(Ts&&...)
{
    return {};
}
70
krzaq

Bien que la solution de krzaq soit plutôt bonne, je pense que l'implémentation de la "magie" derrière sizeof... peut être un exercice d'apprentissage intéressant.

Il utilise une technique très courante pour la méta-programmation de modèle - une fonction non-modèle couvrant le cas de base et une fonction de modèle qui réduit le problème d'une étape:

// Base case
int count_args() {
    return 0;
}
// Reduction
template<typename T, typename... Vs>
int count_args(T _, Vs... vs) {
    return 1 + count_args(vs...);
}

Avec cette fonctionnalité en place, vous pouvez implémenter le vérificateur de divisibilité en utilisant l'approche de la réponse de krzaq:

template<int N,typename... Vs>
bool is_arg_divisible(Vs... vs) {
    return count_args(vs...) % N == 0;
}

Démo.

27
dasblinkenlight