Echec de la déduction d’argument de modèle lors de l’utilisation de parameters de modèle

Je voulais créer un algorithme auxiliaire simple qui remplirait un conteneur, tel que std::vector , avec une progression géomésortingque (le premier terme est a , et le n ème terme est donné par a * pow(r, n-1) , où r est un rapport donné); J’ai créé le code suivant:

 #include #include #include template<template  class Container, typename T> void progression(Container& container, T a, T ratio, size_t N) { if(N > 0) { T factor = T(1); for(size_t k=0; k<N; k++) { container.push_back(a * factor); factor *= ratio; } } } int main() { std::vector r; progression(r, 10.0, 0.8, static_cast(10)); for(auto item : r) { std::cout<<item<<std::endl; } return 0; } 

qui génère les erreurs suivantes lors d’une tentative de compilation:

 $ g++ geomesortingc.cpp -std=c++11 # GCC 4.7.2 on OS X 10.7.4 geomesortingc.cpp: In function 'int main()': geomesortingc.cpp:18:52: error: no matching function for call to 'progression(std::vector&, double, double, size_t)' geomesortingc.cpp:18:52: note: candidate is: geomesortingc.cpp:6:6: note: template<template class Container, class T> void progression(Container&, T, T, size_t) geomesortingc.cpp:6:6: note: template argument deduction/substitution failed: geomesortingc.cpp:18:52: error: wrong number of template arguments (2, should be 1) geomesortingc.cpp:5:36: error: provided for 'template class Container' 

Le message d’erreur de Clang est plus subtil:

 $ clang++ geomesortingc.cpp -std=c++11 # clang 3.2 on OS X 10.7.4 geomesortingc.cpp:18:3: error: no matching function for call to 'progression' progression(r, 10, 0.8, 10); ^~~~~~~~~~~ geomesortingc.cpp:6:6: note: candidate template ignored: failed template argument deduction void progression(Container& container, T a, T ratio, size_t N) { ^ 1 error generated. 

J’aurais pensé qu’en utilisant les parameters de modèle de modèle, je serais capable de déduire non seulement le conteneur, mais également le type de value_type du value_type ( T dans ce cas).

La question est donc de savoir comment créer une fonction générique permettant de déduire à la fois le type de conteneur et le type de valeur.

Je suis sûr qu’il me manque quelque chose d’évident – j’apprécie votre patience et votre aide.

Modifier (répondre)

Le code suivant se comporte comme prévu:

 #include #include #include template<template  class Container, typename T, typename... Args> void progression(Container& container, T a, T ratio, size_t N) { if(N > 0) { T factor = T(1); for(size_t k=0; k<N; k++) { container.push_back(a * factor); factor *= ratio; } } } int main() { std::vector r; progression(r, 10.0, 0.8, 10); for(auto item : r) { std::cout<<item<<std::endl; } return 0; } 

Sortie:

 10 8 6.4 5.12 4.096 3.2768 2.62144 2.09715 1.67772 1.34218 

Le premier problème est que vous oubliez que std::vector<> est un modèle de classe acceptant deux parameters de modèle (le type d’élément et l’allocateur), pas un. Le fait que le deuxième paramètre de modèle ait une valeur par défaut est sans importance lorsque vous utilisez des parameters de modèle de modèle:

 template 

Notez que cela rendra impossible la transmission, par exemple, d’une instance de std::map ou de std::unordered_map tant que premier argument de fonction. Par conséquent, ma suggestion est de renoncer à déduire que le premier argument est l’instance d’un conteneur standard (les conteneurs standard ne sont tout simplement pas aussi uniformes):

 template // ^^^^^^^^^ void progression(C& container, T a, T ratio, size_t N) { // ^^ // ... } 

Ce que vous voudrez peut-être alors faire, c’est d’exprimer la contrainte de compilation, peut-être via un static_assert et sur la base d’un trait de type personnalisé, en indiquant que C doit être une instance d’un conteneur standard.

Alternativement, vous pouvez utiliser des modèles variadiques comme suggéré par KerrekSB dans sa réponse (mais cela ne vous empêchera pas de transmettre une instance d’un autre type de modèle, même non-conteneur).

Le deuxième problème réside dans la façon dont vous appelez votre modèle:

 progression(r, 10, 0.8, 10); 

Ici, le type du deuxième argument est int , alors que le type de l’élément conteneur est double . Cela confondra le compilateur lors de l’exécution de la déduction de type. Soit appeler ça comme ça:

 progression(r, 10.0, 0.8, 10); 

Vous pouvez également autoriser votre compilateur à déduire un type différent pour le second argument (SFINAE possible le contraignant à être converti en type d’élément).

Les conteneurs ont généralement beaucoup d’arguments de template. Heureusement, il existe une clause spéciale dans les modèles variadiques qui permet d’utiliser un pack pour n’importe quel nombre concret d’arguments:

 template  

(La clause spéciale indique que cela fonctionne même lorsque Container est un modèle non variadique.)

La raison pour laquelle cela échoue est parce que vous dites que deux arguments du milieu sont du même type alors qu’ils ne le sont pas. Par exemple, le milieu est un float tandis que l’autre est un int. Fondamentalement, vous dites qu’un rapport et sont du même type, mais dans l’appel, ils sont de types différents