2016-12-31 1 views
6

J'ai besoin de calculer le produit d'un tas de nombres au moment de la compilation passé à une structure modèle. Je parvins à faire une solution laide :template Métaprogrammation: multiplier un tas d'arguments de template

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>; 
}; 
template<> 
struct mul_all<0> 
{ 
    static constexpr std::size_t value = 1; 
}; 


Le problème est que chaque fois que je dois nourrir 0 à modèle args à mon struct comme si

int main() 
{ 
    std::cout << mul_all<1,2,5,4,5,7,0>::value << " " 
       << mul_all<4,2,0>::value; 
    return 0; 
} 


est-il une solution de contournement pour lire ce dernier zéro?

note: Je suis un débutant en TMP.

+3

Juste pour voir, voici un C + +14 Solution 'constexpr' qui n'utilise pas la récursivité des modèles: http://melpon.org/wandbox/permlink/yNbfyOhiN3hLqmpA – bogdan

+0

cool !!! est-il possible de la comparer avec l'autre solution? –

+0

Voulez-vous dire en termes de temps de compilation? Les solutions non récursives devraient être à peu près les mêmes, et mieux que les classiques impliquant la récursivité des templates, car les récursives produisent plusieurs instanciations de templates, ce qui coûte quelque chose (en pratique, cela commence à importer pour un nombre relativement important d'arguments template). des dizaines d'entre eux). Cependant, la solution de tableau factice C++ 14 est juste une solution de contournement pour l'absence d'expressions de pli; Je choisirais C++ 17 fois les expressions disponibles. – bogdan

Répondre

6

Dans 17 C de, avec le pliage d'expression, vous pouvez directement faire

template<std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = (args * ...); 
}; 

Avant, vous devez faire la spécialisation partielle:

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

template<std::size_t n> 
struct mul_all<n> 
{ 
    static constexpr std::size_t value = n; 
}; 
+0

votre proposition est cool mais elle n'a pas compilé malheureusement, j'ai copié et passé le code, testé avec GCC 7.0.0 snapshot mais il n'a pas passé. avez-vous supprimé intentionnellement ':: value' d'ici' statique constexpr std :: taille_t valeur = n1 * mul_all ; ' –

+0

@chedynajjar: En effet' :: value' manquait dans le second snippet, corrigé. – Jarod42

5

Vous devez remplacer votre spécialisation avec:

template<std::size_t n1, std::size_t ...args> 
struct mul_all 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

template<std::size_t n> 
struct mul_all<n> 
{ 
    static constexpr std::size_t value = n; 
}; 
+0

vous n'avez aucune récursion dans votre solution et je ne vois même pas d'arguments varidiques. –

+0

@chedynajjar: c'est un remplacement de votre spécialisation complète par une spécialisation partielle: tout reste comme dans votre approche. –

+0

@ DietmarKühl: J'ai une erreur de compilation: 'erreur: nombre incorrect d'arguments de modèle (0, devrait être au moins 1) statique constexpr std :: taille_t valeur = n1 * mul_all :: valeur;' –

3

Une façon est de se spécialiser pour varargs vides. Pour cela, vous devez le principal modèle pour être args variadique seulement:

// main template never used 
template<std::size_t ...args> struct mul_all 
{ 
}; 

// specialization for at least one arg 
template<std::size_t n1, std::size_t ...args> 
struct mul_all<n1, args...> 
{ 
    static constexpr std::size_t value = n1 * mul_all<args...>::value; 
}; 

// specialization for empty args 
template<> 
struct mul_all<> 
{ 
    static constexpr std::size_t value = 1; 
}; 

vous pouvez faire Alors maintenant:

+0

C'est la bonne façon de faire aussi loin que la récursivité des modèles va. Il n'est pas nécessaire d'avoir à passer le nombre d'arguments de modèle en tant que paramètre de modèle. –

2

La C approche ++ 17 font que simple et agréable:

template <std::size_t... A> 
constexpr std::size_t mul = (A * ... * std::size_t(1u)); 

int main() { 
    constexpr std::size_t val = mul<1, 2, 3, 4>; 
} 

Pour les versions existantes de C++, vous aurez besoin de se spécialiser en partie le cas mul<v>:

template <std::size_t... V> struct mul; 
template <std::size_t V> struct mul { 
    statuc constexpr std::size_t value = V; 
}; 
template <std::size_t V, std::size_t... T> struct mul { 
    statuc constexpr std::size_t value = V * mul<T...>::value; 
}; 
template <std::size_t... V> 
using mul_v = mul<V...>::value; 

int main() { 
    constexpr std::size_t v = mul_v<1, 2, 3, 4>; 
} 
+1

Je suppose que la version C++ 17 est plus proche de '(A * ... * 1U)'. – bogdan

+0

@bogdan: oui, en effet - actuellement je ne peux pas facilement compiler mais j'espère qu'il est corrigé maintenant. Merci! –

+0

Vous avez besoin de parens autour de l'expression du pli, ils sont obligatoires dans la grammaire. – bogdan