Outils pour utilisateurs

Outils du site


lang:cpp:template

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
lang:cpp:template [2020/04/26 23:00] – Conversion de <note> vers <WRAP> rootlang:cpp:template [2025/02/05 10:07] (Version actuelle) – [const Args...] : fix typo root
Ligne 1: Ligne 1:
 +=====Restriction=====
 +
 +====Auto déduction====
 +
 +Il n'est pas possible de faire de l'auto-déduction du retour d'une fonction.
 +
 +<code cpp>
 +typename<template R>
 +R fonction()
 +{
 +  return 1;
 +}
 +</code>
 +
 +Il faut utiliser ''auto'' à la place.
 +
 +<code cpp>
 +auto fonction()
 +{
 +  return 1;
 +}
 +</code>
 +
 +====const Args...====
 +
 +Il faut éviter les ''const'' sur les templates variadiques.
 +
 +Sinon, cela impose l'utilise de const partout et ainsi, le code ci-dessous devient invalide (même s'il compile quand même) car le compilateur passe le nombre en tant que ''double'' et non ''const double''.
 +
 +<code cpp>
 +template<typename ...Args>
 +void f(const Args&&... args){}
 +
 +f(1.2);
 +</code>
 +
 +J'ai déjà eu des codes plus complexes où cela posait problème.
 +
 =====Héritage===== =====Héritage=====
 ====CRTP (Curiously recurring template pattern)==== ====CRTP (Curiously recurring template pattern)====
Ligne 254: Ligne 292:
  
 <blockquote>On utilisera donc l’héritage quand c’est nécessaire, rappelez vous “un développeur est une personne”. On passera aux mixins quand on est dans la situation “un développeur agit comme un salarié”. On se tournera probablement vers la composition si cette relation s’avère être quelque chose de complexe, un objet nécessitant une classe dédiée.<cite>[[https://www.synbioz.com/blog/tech/la-composition-a-la-rescousse-de-lheritage|La composition à la rescousse de l'héritage]] {{ :lang:cpp:template:la_composition_a_la_rescousse_de_l_heritage_2020-03-12_23_02_42_.html |Archive du 18/02/2016 le 12/03/2020}}</cite></blockquote> <blockquote>On utilisera donc l’héritage quand c’est nécessaire, rappelez vous “un développeur est une personne”. On passera aux mixins quand on est dans la situation “un développeur agit comme un salarié”. On se tournera probablement vers la composition si cette relation s’avère être quelque chose de complexe, un objet nécessitant une classe dédiée.<cite>[[https://www.synbioz.com/blog/tech/la-composition-a-la-rescousse-de-lheritage|La composition à la rescousse de l'héritage]] {{ :lang:cpp:template:la_composition_a_la_rescousse_de_l_heritage_2020-03-12_23_02_42_.html |Archive du 18/02/2016 le 12/03/2020}}</cite></blockquote>
 +
 +=====Divers=====
 ====Avantages/inconvénient du code source dans les fichiers entête==== ====Avantages/inconvénient du code source dans les fichiers entête====
   * Avantages   * Avantages
Ligne 266: Ligne 306:
  
 ====Séparer le code source des fonctions et leur définition dans une classe template==== ====Séparer le code source des fonctions et leur définition dans une classe template====
 +
 +  * Exemple avec une classe template
 +
 C'est possible de ne pas être obligé de rendre ''inline'' toutes les méthodes d'une classe ''template''. Mais il faut alors explicitement indiquer quelles combinaisons ''class'' / ''template'' seront utilisées par un ''template class XX<YY>;'' C'est possible de ne pas être obligé de rendre ''inline'' toutes les méthodes d'une classe ''template''. Mais il faut alors explicitement indiquer quelles combinaisons ''class'' / ''template'' seront utilisées par un ''template class XX<YY>;''
  
Ligne 285: Ligne 328:
 void T<X>::fff() {} void T<X>::fff() {}
  
 +// On instancie uniquement T<int> (et donc T<int>::fff()).
 template class T<int>; template class T<int>;
 </file> </file>
Ligne 294: Ligne 338:
 <file cpp main.cc> <file cpp main.cc>
 #include "template.h" #include "template.h"
- 
-extern template class T<int>(); 
  
 int main() int main()
 { {
   T<int> tint;   T<int> tint;
-  return tint.retval();+  return tint.fff();
 } }
 </file> </file>
  
-====Afficher en string le type template==== +  * Exemple avec une fonction template
-<code cpp> +
-typeid(T).name(); +
-</code>+
  
-[[https://stackoverflow.com/questions/4484982/how-to-convert-typename-t-to-string-in-c|How to convert typename T to string in c++]] {{ :lang:cpp:template:templates_-_how_to_convert_typename_t_to_string_in_c_-_stack_overflow_2019-09-04_11_16_03_.html |Archive du 04/09/2019}} +<file cpp file.h> 
- +class 
-[[https://raw.githubusercontent.com/gelldur/common-cpp/master/src/acme/type_name.h|type_name.h]] {{ :lang:cpp:template:type_name.h |Archive du 04/09/2019}} +    template<typename U> 
- +    void f();
-[[https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/|Is it possible to print a variable's type in standard C++?]] {{ :lang:cpp:template:is_it_possible_to_print_a_variable_s_type_in_standard_c_-_stack_overflow_2019-09-04_11_22_58_.html |Archive du 04/09/2019}} +
- +
-====Décorateur==== +
- +
-[[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}} +
- +
-  * implicit return type +
-<code cpp> +
-#include <iostream> +
-#include <memory> +
- +
-struct Action1 { }; +
-struct Action2 { }; +
- +
-class Classe +
-{ +
-public: +
-    // Default template. +
-    template<class U, class ...Args+
-    auto go(Args ...args) +
-    { +
-        //static_assert(false); +
-    }+
 }; };
 +</file>
  
-// Action 1+<file cpp file.cpp> 
-template<> +template<typename Uvoid A::f(){}
-auto Classe::go<Action1>() +
-{ +
-    std::cout << "coucouGo1" << std::endl; +
-}+
  
-// Action 2. +// Instanciation pour U=int 
-template<> +template void A::f<int>();
-auto Classe::go<Action2>(int i) +
-+
-    std::cout << "coucouGo2 : " << static_cast<unsigned char>(i<< std::endl; +
-    return static_cast<unsigned char>(i); +
-}+
  
-template <class T> +// Spécialisation pour U=double 
-class Decorator +template<> void A::f<double>(){} 
-+</file>
-public: +
-    Decorator(std::unique_ptr<Tt):classe(std::move(t)) {}+
  
-    template<class U, class... Args> +  * Exemple avec une fonction template dans une classe template
-    auto go(Args ...args) +
-    { +
-        std::cout << "coucouDecorator" << std::endl; +
-        return classe->template go<U>(args...); +
-    } +
-    private: +
-        std::unique_ptr<T> classe; +
-}; +
-</code>+
  
-  * explicit return type +<file cpp file.h
- +template<typename T
-<code cpp> +class 
-#include <iostream> +    template<typename U> 
-#include <memory> +    void f(U u);
- +
-struct Action1 { using Type = void; }; +
-struct Action2 { using Type = char; }; +
- +
-class Classe +
-{ +
-public: +
-    // Default template. +
-    template<class U, class T, class ...Args+
-    T go(Args ...args) +
-    { +
-        //static_assert(false); +
-    }+
 }; };
- 
-// Action 1. 
-template<> 
-Action1::Type Classe::go<Action1>() 
-{ 
-    std::cout << "coucouGo1" << std::endl; 
-} 
- 
-// Action 2. 
-template<> 
-Action2::Type Classe::go<Action2>(int i) 
-{ 
-    std::cout << "coucouGo2 : " << i << std::endl; 
-    return i; 
-} 
- 
-template <class T> 
-class Decorator 
-{ 
-public: 
-    Decorator(std::unique_ptr<T> t):classe(std::move(t)) {} 
- 
-    template<class U, class... Args> 
-    auto go(Args ...args) 
-    { 
-        std::cout << "coucouDecorator" << std::endl; 
-        return classe->template go<U, typename U::Type>(args...); 
-    } 
-    private: 
-        std::unique_ptr<T> classe; 
-}; 
-</code> 
- 
-  * main.cc 
- 
-<code cpp> 
-int main() 
-{ 
-    Decorator<Classe> c(std::make_unique<Classe>()); 
-    c.go<Action1>(); 
-    c.go<Action2>(2); 
-} 
-</code> 
- 
-====metaprogrammation vs constexpr==== 
-===Exemple simple=== 
-<code cpp> 
-#include <cstdio> 
- 
-template <typename T> 
-constexpr T square(T x) { 
-  return x*x; 
-} 
- 
-int main() { 
-  printf("%lf %d\n", square(3.4), square<int>(3.4)); 
-  // 11.560000 9 
-} 
-</code> 
- 
-===Fibonacci=== 
- 
-  * Template 
- 
-<file cpp fibonacci.cc> 
-template <long N> struct fibonacci 
-{ 
-  static const long value = fibonacci<N - 1>::value + fibonacci<N - 2>::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; 
-} 
 </file> </file>
  
-gcc et clang génère 190392490709135 avec succès.+<file cpp file.cpp> 
 +template<typename T> template<typename U> void A<T>::f(U u){}
  
-  * constexpr +template void A<int>::f(short u);
- +
-<file cpp fibonacci2.cc> +
-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; +
-}+
 </file> </file>
  
-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.+  * Messages d'erreur
  
-<WRAP center round important 60%> +''error: specialization of XXX after instantiation''
-Le code est beaucoup moins optimisé dans cette version 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. +
-</WRAP>+
  
-===Nombres premiers===+''error: explicit specialization of XXX after instantiation''
  
-  * Template+''error C2908: explicit specialization; XXX has already been instantiated''
  
-[[https://www.youtube.com/watch?v=EkdfiHs78DY|CppCon 2016: Peter Gottschling “How bad is Meta-Programming still today?"]]+Il ne faut pas utiliser une classe spécialisée avant qu'elle ne soit définie.
  
-[[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}}+<code cpp
 +template <typename T> 
 +class A {};
  
-<file cpp prime.cc> +// Doit être défini après la spécialisation. 
-#include <type_traits> +A<int> a;
- +
-template <int N> +
-struct Sqrt { +
-    template <int lo, int hi> +
-    struct Helper { +
-        static const int mid = (lo + hi + 1) 2; +
-        static const int value = std::conditional < (N mid < mid), Helper < lo, mid - 1 >, Helper <mid, hi> >::type::value; +
-    }; +
-    template <int n> +
-    struct Helper <n, n> { +
-        static const int value = n; +
-    }; +
-    static const int value = Helper <0, N>::value; +
-}; +
- +
-template <long x, long max_odd> struct is_prime_to_max_odd { +
-  static bool const value = x % max_odd != 0 && +
-      is_prime_to_max_odd<x, max_odd-2>::value; +
-}; +
- +
-template <long x> struct is_prime_to_max_odd<x, 1> : std::true_type {}; +
- +
-template <long x> struct max_prime_compare { +
-  static long const tmp = Sqrt<x>::value, value = tmp % 2 == 0 ? tmp + 1 : tmp + 2; +
-}; +
- +
-template <long x, bool disable> struct check_odd { +
-  static bool const value = is_prime_to_max_odd<x, max_prime_compare<x>::value>::value; +
-}; +
- +
-template <long x> struct check_odd <x, true> { +
-  static bool const value = false; +
-}; +
- +
-template <long x> struct is_prime { +
-  static bool const value = check_odd<x, x%2==0>::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; +
-+
-</file> +
- +
-gcc et clang arrive à calculer à la compilation. +
- +
-====Appeler la méthode généralisée depuis la méthode spécialisée==== +
-C'est impossible. Il faut que la méthode de base appelle une méthode spécifique.+
  
 +template <>
 +class A<int> {};
 +</code>
 +====Afficher en string le type template====
 <code cpp> <code cpp>
-template<typename T> +typeid(T).name(); 
-void baseF(T t... }+</code>
  
-template<typename T+[[https://stackoverflow.com/questions/4484982/how-to-convert-typename-t-to-string-in-c|How to convert typename T to string in c++]] {{ :lang:cpp:template:templates_-_how_to_convert_typename_t_to_string_in_c_-_stack_overflow_2019-09-04_11_16_03_.html |Archive du 04/09/2019}}
-void F(T t) baseF<T>(t); }+
  
-template<> +[[https://raw.githubusercontent.com/gelldur/common-cpp/master/src/acme/type_name.h|type_name.h]] {{ :lang:cpp:template:type_name.h |Archive du 04/09/2019}}
-void F<int>(int t) baseF<int>(t);+
-</code>+
  
-[[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}}+[[https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/|Is it possible to print a variable's type in standard C++?]] {{ :lang:cpp:template:is_it_possible_to_print_a_variable_s_type_in_standard_c_-_stack_overflow_2019-09-04_11_22_58_.html |Archive du 04/09/2019}}
  
 ====class ou typename==== ====class ou typename====
Ligne 730: Ligne 563:
   AA2<Types>::ff();   AA2<Types>::ff();
 } }
 +</code>
 +
 +====Sérialisation====
 +
 +Compter le nombre de champ d'une classe.
 +
 +<code cpp>
 +#include <iostream>
 +
 +struct UniversalType {
 +    template <typename T>
 +    operator T();  // no definition required
 +};
 +
 +template <typename T, typename... A0>
 +consteval auto MemberCounter(auto... c0) {
 +    if constexpr (requires { T{{A0{}}..., {UniversalType{}}, c0...}; })
 +        return MemberCounter<T, A0..., UniversalType>(c0...);
 +    else if constexpr (
 +        requires {
 +            T{{A0{}}..., {UniversalType{}}, c0...};
 +        } ||
 +        requires {
 +            T{{A0{}}..., c0..., UniversalType{}};
 +        })
 +        return MemberCounter<T, A0...>(c0..., UniversalType{});
 +    return sizeof...(A0) + sizeof...(c0);
 +}
 +
 +int main() {
 +    using TestType = struct {
 +        int x[3];
 +        float y;
 +        char z;
 +    };
 +    auto [a, b, c] = TestType{};                          // decomposes into 3
 +    std::cout << MemberCounter<TestType>() << std::endl;  // prints 3
 +}
 +</code>
 +
 +====Erreurs====
 +
 +  * Il manque un mot clé ''typename''
 +
 +Il faut parfois rajouter le mot clé ''typename'' quand un type est suivi du symbole ''<''. Cela permet au compilateur de différentier l'opérateur de comparaison ''<'' avec le symbole permettant d'explicité un type dans un template.
 +
 +<code cpp>
 +error: 'GetB_' does not refer to a value
 +    return T::template f<MeyerhofShallowFoundationImpl<U, V>::GetB_>(
 +                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
 +</code>
 +
 +<code cpp>
 +    return T::template f<typename MeyerhofShallowFoundationImpl<U, V>::GetB_>(
 +</code>
 +
 +  * Il manque un mot clé ''template''
 +
 +<code cpp>
 +error: expected ';' after expression
 +      retval->impl_ = impl_->f<Action, U...>(std::forward<const Args>(args)...);
 +                                        ^
 +                                        ;
 +</code>
 +
 +ou encore
 +
 +<code cpp>
 +error: expression contains unexpanded parameter pack 'U'
 +      retval->impl_ = impl_->f<Action, U...>(std::forward<const Args>(args)...);
 +      ^                                ~
 +</code>
 +
 +ou encore
 +
 +<code cpp>
 +error: expected primary-expression before ')' token
 +    return this->deco_->f<123, 456>();
 +                                           ^
 +</code>
 +
 +Solution:
 +
 +<code cpp>
 +      retval->impl_ = impl_->template f<Action, U...>(std::forward<const Args>(args)...);
 +</code>
 +
 +===Mapping d'un type vers un autre via une map===
 +
 +https://stackoverflow.com/questions/68668956/c-how-to-implement-a-compile-time-mapping-from-types-to-types
 +
 +<code>
 +using my_map = type_map<
 +    pair<int, float>,
 +    pair<char, double>,
 +    pair<long, short>
 +>;
 +
 +static_assert(std::is_same_v<my_map::find<int>, float>);
 +static_assert(std::is_same_v<my_map::find<char>, double>);
 +static_assert(std::is_same_v<my_map::find<long>, short>);
 +</code>
 +
 +<code cpp>
 +template <typename T>
 +struct type_tag
 +{
 +  using type = T;
 +};
 +
 +template <typename K, typename V>
 +struct pair
 +{
 +  using first_type = K;
 +  using second_type = V;
 +};
 +
 +template <typename Pair>
 +struct element
 +{
 +  static auto value(type_tag<typename Pair::first_type>)
 +      -> type_tag<typename Pair::second_type>;
 +};
 +
 +template <typename... elems>
 +struct type_map : element<elems>...
 +{
 +  using element<elems>::value...;
 +
 +  template <typename K>
 +  using find = typename decltype(type_map::value(type_tag<K>{}))::type;
 +};
 </code> </code>
lang/cpp/template.1587934809.txt.gz · Dernière modification : 2020/04/26 23:00 de root