=====Spécialisation complète===== ====Appeler la méthode généralisée depuis la méthode spécialisée==== C'est impossible. La solution est que la méthode de base s'appelle autrement. template void baseF(T t) { ... } template void F(T t) { baseF(t); } template<> void F(int t) { baseF(t); } [[https://stackoverflow.com/questions/6674795/how-to-call-generic-template-function-in-a-specialization-version|How to call generic template function in a specialization version]] {{ :lang:cpp:template:c_-_how_to_call_generic_template_function_in_a_specialization_version_-_stack_overflow_2020-02-06_23_35_03_.html |Archive du 13/07/2011 le 06/02/2020}} ====Empêcher l'utilisation du template non spécialisé==== ''static_assert(false)'' ne peut être utilisé directement car il est systématiquement évalué par le compilateur. Il faut ajouter un niveau d'indirection. #include template struct foobar : std::false_type {}; // Template général. template T fun(T a) { // La variable value n'est évaluée que si la méthode est utilisée. static_assert(foobar::value, "this function has to be implemented for desired type"); } template <> int fun(int a) { return a; } int main() { // fun('a'); // Echec fun(10); // fun(10.14); // Echec } Autre exemple avec ''std::conditional'': #include struct A { using Ty = int; }; struct B {}; // KO, should be OK. // using Ttrue = std::conditional_t; // KO // using Tfalse = std::conditional_t; template struct LazyLoadIdentity { using type = T; }; template struct LazyLoadTy : LazyLoadIdentity {}; // OK using Ttrue = std::conditional_t, LazyLoadTy>::type; // KO //using Tfalse = std::conditional_t, LazyLoadTy>::type; [[https://stackoverflow.com/questions/34281017/is-it-possible-to-build-a-lazy-conditional-metafunction|Is it possible to build a lazy conditional metafunction]] {{ :lang:cpp:template:c_-_is_it_possible_to_build_a_lazy_conditional_metafunction_-_stack_overflow_2021-05-17_22_08_45_.html |Archive du 15/12/2015 le 17/05/2021}} ====Exemples==== ===Décorateur (spécialisation sur type)=== [[https://cppcodetips.wordpress.com/2016/10/31/decorator-pattern-explained-with-c-sample/|Decorator Pattern Explained with C++ sample]] {{ :lang:cpp:template:decorator_pattern_explained_with_c_sample_cpp_code_tips_2020-02-05_21_09_42_.html |Archive du 31/10/2016 le 05/02/2020}} #include #include struct Action1 { }; struct Action2 { }; class Classe { public: // Default template. template auto go(Args ...args) { //static_assert(false); } }; // Action 1. template<> auto Classe::go() { std::cout << "coucouGo1" << std::endl; } // Action 2. template<> auto Classe::go(int i) { std::cout << "coucouGo2 : " << static_cast(i) << std::endl; return static_cast(i); } template class Decorator { public: Decorator(std::unique_ptr t):classe(std::move(t)) {} template auto go(Args ...args) { std::cout << "coucouDecorator" << std::endl; return classe->template go(args...); } private: std::unique_ptr classe; }; int main() { Decorator c(std::make_unique()); c.go(); c.go(2); } ===Fibonacci (spécialisation sur valeur)=== * Template template struct fibonacci { static const long value = fibonacci::value + fibonacci::value; }; template <> struct fibonacci<1> { static const long value = 1; }; template <> struct fibonacci<2> { static const long value = 1; }; int main() { long i = fibonacci<70>::value; return 0; } gcc et clang génère 190392490709135 avec succès. * constexpr constexpr long fib(long n) { if (n <= 2) { return 1; } return fib(n - 1) + fib(n - 2); } int main() { constexpr long i = fib(70); return 0; } gcc arrive à calculer jusqu'à la valeur 35 et clang jusqu'à 26. Au delà, il faut utiliser ''-fconstexpr-ops-limit='' (2^25 par défaut) pour gcc et ''-fconstexpr-backtrace-limit=80 -fconstexpr-steps=2147483647'' pour clang. Le code est beaucoup moins optimisé avec constexpr. Il aurait fallu d'abord remplir un tableau de 70 valeurs (ce que fait le compilateur dans la première version en calculant les ''static value'') ce qui aurait permis d'éviter d'avoir une récursion très longue de 70 étapes. ===Nombres premiers (spécialisation par valeur)=== * Template [[https://www.youtube.com/watch?v=EkdfiHs78DY|CppCon 2016: Peter Gottschling “How bad is Meta-Programming still today?"]] [[https://codeforces.com/blog/entry/15310|C++11 Template Metaprogramming — Compile Time Computations]] {{ :lang:cpp:template:c_11_template_metaprogramming_compile_time_computations_-_codeforces_2020-01-17_10_32_25_am_.html |Archive du 21/12/2014 le 17/01/2020}} #include template struct Sqrt { template struct Helper { static const int mid = (lo + hi + 1) / 2; static const int value = std::conditional < (N / mid < mid), Helper < lo, mid - 1 >, Helper >::type::value; }; template struct Helper { static const int value = n; }; static const int value = Helper <0, N>::value; }; template struct is_prime_to_max_odd { static bool const value = x % max_odd != 0 && is_prime_to_max_odd::value; }; template struct is_prime_to_max_odd : std::true_type {}; template struct max_prime_compare { static long const tmp = Sqrt::value, value = tmp % 2 == 0 ? tmp + 1 : tmp + 2; }; template struct check_odd { static bool const value = is_prime_to_max_odd::value>::value; }; template struct check_odd { static bool const value = false; }; template struct is_prime { static bool const value = check_odd::value; }; template <> struct is_prime <1> : std::false_type {}; template <> struct is_prime <2> : std::true_type {}; int main() { bool b49991 = is_prime<49991>::value; bool b49992 = is_prime<49992>::value; bool b49993 = is_prime<49993>::value; return 0; } gcc et clang arrive à calculer à la compilation. =====Spécialisation partielle===== ====Template imbriqué==== Il faut spécialiser le template parent avant de pouvoir spécialiser le template enfant. * Classe template template class A { template class B { }; }; // OK template<> template class A::B { int b; }; // KO template template<> class A::B { int b; }; ====Fonction template==== Il n'est pas possible de faire de la spécialisation partielle de template comme pour les classes. * ''enable_if'' Il est possible d'activer certaines spécialisations de template en fonction du type. Mais c'est de la surcharge, pas de la spécialisation. Il faut bien vérifier que les ''enable_if'' soit bien mutuellement exclusifs donc il ne doit pas y avoir le fonction générique mais uniquement des fonctions spécialisées. ''std::enable_if'' permet d'activer sous condition une fonction template. Le premier argument est la condition, le deuxième argument est le type de retour de la fonction. #include struct I { using R = int; }; struct F { using R = float; }; class A { public: template static typename std::enable_if_t, I::R> f() { return 1; } template static typename std::enable_if_t, F::R> f() { return 27; } }; int main() { return A::f() + A::f(); } * Utilisation d'une classe Si la méthode est static, le plus simple est de passer par des classes qui ne contionnent qu'une seule méthode, celle à spécialiser partiellement. template struct A { static int foo(U) { return 1; } }; template struct A { static int foo(int) { return 2; } };