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/03/08 13:01] – [Boucles sur des types] : simplification de fold rootlang:cpp:template [2025/02/05 10:07] (Version actuelle) – [const Args...] : fix typo root
Ligne 1: Ligne 1:
-====Avantages/inconvénient du code source dans les fichiers entête==== +=====Restriction=====
-  * Avantages +
-    * Pas besoin d'avoir d'un coté la déclaration des classes (entête) et de l'autre l'implémentation. +
-    * Pour les templates, le compilateur va générer automatiquement le code pour chaque symbole pour chaque variante d'un template. En mettant le code dans l'entête, on est sûr de ne pas avoir des ''undefined reference to XXX'' en oubliant d'instancier un template extern. +
-    * Pas besoin d'utiliser des extern template. +
-  * Inconvénient +
-    * Les fichiers entête sont plus dur à lire pour trouver la liste des méthodes publiques. Sinon, il est possible de déclarer les méthodes dans la classe et de définir les méthodes inline en dessous de la classe. +
-    * Le compilateur va générer le code des fonctions dans chaque fichier source où le template est utilisé. +
-    * Le nombre de fichiers inclus dans les entêtes sera (sauf exception) plus important que si on avait laissé uniquement la déclaration de la classe dans le fichier entête. En effet, certains entrées ne sont peut-être nécessaire que pour l'implémentation et par pour le prototype des méthodes. +
-    * Le lieur va avoir de nombreux fichiers objets ayant les mêmes symboles de type Weak. Il va donc devoir faire beaucoup de nettoyage et le compilateur aura beaucoup travaillé pour rien. Pire, si le code de la méthode template est dépendant d'une macro qui varie entre plusieurs fichiers cpp, seules une des implémentations sera retenue puis généralisée.+
  
-====Séparer le code source des fonctions et leur définition dans une classe template==== +====Auto déduction====
-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>;''+
  
-[[https://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possible | Splitting templated C++ classes into .hpp/.cpp files--is it possible?]] {{ :lang:cpp:template:class_-_splitting_templated_c_classes_into_.hpp_.cpp_files--is_it_possible_-_stack_overflow_2019-08-27_08_55_46_.html |Archive du 27/08/2019}}+Il n'est pas possible de faire de l'auto-déduction du retour d'une fonction.
  
-<file cpp template.h+<code cpp> 
-template<class X+typename<template R
-class T+R fonction()
 { {
-public: +  return 1
-   void fff()+
-}; +</code>
-</file>+
  
-<file cpp template_impl.cc> +Il faut utiliser ''auto'' à la place.
-#include "template.h"+
  
-template<class X+<code cpp
-void T<X>::fff() {}+auto fonction() 
 +{ 
 +  return 1; 
 +} 
 +</code>
  
-template class T<int>; +====const Args...====
-</file>+
  
-<note important>Si on décide de mettre le ''template class'' dans le même fichier que le ''.cc'', il est indispensable de le mettre tout en bas du fichier, après la définition de toutes les fonctions sinon les fonctions qui peuvent être ''inline'' ne seront pas conservées en symbole.</note>+Il faut éviter les ''const'' sur les templates variadiques.
  
-<file cpp main.cc> +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''.
-#include "template.h"+
  
-extern template class T<int>(); +<code cpp
- +template<typename ...Args
-int main() +void f(const Args&&... args){}
-+
-  T<inttint; +
-  return tint.retval()+
-} +
-</file>+
  
-====Afficher en string le type template==== +f(1.2);
-<code cpp> +
-typeid(T).name();+
 </code> </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}}+J'ai déjà eu des codes plus complexes où cela posait problème.
  
-[[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}}+=====Héritage===== 
 +====CRTP (Curiously recurring template pattern)====
  
-[[https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/|Is it possible to print a variable'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}}+===Principe=== 
 +Le pattern CRTP fait de l'héritage inverseC'est dans le parent qu'on ajoute les fonctionnalités. Cela a comme avantage de s'affranchir des méthode virtuelles et le gain de performance résulte dans le fait que les méthodes sont optimisées par le compilateur via les fonctions inline.
  
-====Décorateur====+Principe du CRTP : on définit l'appel des fonctions dans la classe de base (qui est ''template''). On accède à la classe enfant via un ''cast'' du ''template''. La classe dérivée hérite de la classe de base avec elle même comme ''template''.
  
-[[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> <code cpp>
-#include <iostream+template <typename T
-#include <memory>+class CRTPBase 
 +
 +  private: 
 +    T& impl() { return *static_cast<T*>(this); } 
 +};
  
-struct Action1 { }; +class CRTPDerived : public CRTPBase<CRTPDerived> 
-struct Action2 { };+{ 
 +}; 
 +</code> 
 + 
 +[[https://github.com/kumarh1982/benchmarks/tree/master/virtual_methods_vs_crtp|Benchmark]] {{ :lang:cpp:class:benchmarks-master-2016-04-26.zip |Archive du 26/04/2016 le 10/03/2020}
 + 
 +===Virtual sans CRTP===
  
-class Classe+<code cpp> 
 +class VirtualBase
 { {
-public: +  public : 
-    // Default template. +    virtual int tick(int n= 0;
-    template<class U, class ...Args> +
-    auto go(Args ...args) +
-    { +
-        //static_assert(false); +
-    }+
 }; };
  
-// Action 1. +class VirtualDerived public VirtualBase
-template<> +
-auto Classe::go<Action1>()+
 { {
-    std::cout << "coucouGo1" << std::endl+  public : 
-}+    int m_counter; 
 +  public : 
 +    VirtualDerived() m_counter(0) {} 
 +    int tick(int n) { m_counter += nreturn m_counter; } 
 +};
  
-// Action 2. +int test_virtual_methods (int test_run)
-template<> +
-auto Classe::go<Action2>(int i)+
 { {
-    std::cout << "coucouGo2 : " << static_cast<unsigned char>(i) << std::endl+  VirtualBase* pObj = static_cast<VirtualBase*>(new VirtualDerived); 
-    return static_cast<unsigned char>(i);+  for( int = 0 ; i < test_run; i++ ) 
 +  { 
 +    for( int j = 0 ; j test_runj++ ) 
 +    
 +      pObj->tick(j); 
 +    } 
 +  } 
 +  return static_cast<VirtualDerived*>(pObj)->m_counter;
 } }
  
-template <class T> +int main (int argc, char** argv)
-class Decorator+
 { {
-public: +  return test_virtual_methods(2000); 
-    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>(args...); +
-    } +
-    private: +
-        std::unique_ptr<T> classe; +
-};+
 </code> </code>
  
-  * explicit return type+===CRTP sans virtual===
  
 <code cpp> <code cpp>
-#include <iostream> +template <typename T
-#include <memory> +class CRTPBase
- +
-struct Action1 { using Type = void; }; +
-struct Action2 { using Type = char; }; +
- +
-class Classe+
 { {
-public: +  public: 
-    // Default template. +    int tick(int n
-    template<class U, class T, class ...Args> +
-    T go(Args ...args)+
     {     {
-        //static_assert(false);+      return impl().tick(n);
     }     }
 +  private:
 +    T& impl() { return *static_cast<T*>(this); }
 }; };
  
-// Action 1. +class CRTPDerived public CRTPBase<CRTPDerived
-template<> +
-Action1::Type Classe::go<Action1>()+
 { {
-    std::cout << "coucouGo1" << std::endl+  public : 
-}+    int m_counter; 
 +  public: 
 +    CRTPDerived() m_counter(0) {} 
 +    int tick(int n) { m_counter += nreturn m_counter; } 
 +};
  
-// Action 2. +int test_crtp (int test_run)
-template<> +
-Action2::Type Classe::go<Action2>(int i)+
 { {
-    std::cout << "coucouGo2 : " << i << std::endl+  CRTPBase<CRTPDerived>* pObj = new CRTPDerived 
-    return i; +  for( int = 0 test_run; i++ ) 
-+  
- +    forint j = 0 ; j test_run; j++ )
-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; +      pObj->tick(j);
-        return classe->template go<U, typename U::Type>(args...);+
     }     }
-    private: +  } 
-        std::unique_ptr<Tclasse+  return static_cast<CRTPDerived*>(pObj)->m_counter
-}+}
-</code>+
  
-  * main.cc +int main (int argc, char** argv)
- +
-<code cpp> +
-int main()+
 { {
-    Decorator<Classe> c(std::make_unique<Classe>()); +  return test_crtp(2000);
-    c.go<Action1>(); +
-    c.go<Action2>(2);+
 } }
 </code> </code>
  
-====metaprogrammation vs constexpr==== +Le problème majeur du CRTP, il n'y a plus de classe parent commune et il est donc impossible de mettre plusieurs implémentations de ''CRTPBase'' dans un même type de pointeur (''std::vector'' par exemple).
-===Exemple simple=== +
-<code cpp> +
-#include <cstdio>+
  
-template <typename T> +J'ai essayé de résoudre ce problème en utilisant un ''std::vector<std::variant<%%...>>%%'' mais son utilisation avec ''std::visit'' casse les performances.
-constexpr T square(T x) { +
-  return x*x; +
-}+
  
-int main() { +===Benchmark===
-  printf("%lf %d\n", square(3.4), square<int>(3.4)); +
-  // 11.560000 9 +
-+
-</code>+
  
-===Fibonacci===+^  Performance    ''-O0 -fno-inline''  ^  ''-O2 -fno-inline''  ^  ''-O2'' 
 +|  Virtuelle      1,695s                0,803s                0,830s   | 
 +|  CRTP          |  3,265s                1,489s                0,130s   |
  
-  * Template+===Héritage multiple===
  
-<file cpp fibonacci.cc+Si on tente de faire un héritage multiple, on va avoir une classe parent commune. 
-template <long N> struct fibonacci+ 
 +clang : ''error: ambiguous cast from base 'crtp<Sensitivity>' to derived 'Sensitivity%%'%%'' 
 + 
 +gcc : ''error: 'crtp<Sensitivity>' is an ambiguous base of 'Sensitivity%%'%%'' 
 + 
 +Visual Studio : ''error C2594: 'static_cast': ambiguous conversions from 'crtp<T>' to 'T &%%'%%'' 
 + 
 +Pour avoir un parent différent, l'astuce est de rajouter un template à crtp qui ne sert à rien. 
 + 
 +<code cpp
 +template<typename T, template<typenametypename> 
 +struct crtp
 { {
-  static const long value = fibonacci<N - 1>::value + fibonacci<N - 2>::value;+  T& underlying() 
 +  { 
 +    return static_cast<T&>(*this); 
 +  } 
 + 
 +  T const& underlying() const 
 +  { 
 +    return static_cast<T const&>(*this); 
 +  }
 }; };
-  + 
-template <> struct fibonacci<1>+template<typename T> 
 +struct Scale : public crtp<T, Scale>
 { {
-  static const long value = 1;+  void scale(double multiplicator) 
 +  { 
 +    this->underlying().setValue(this->underlying().getValue() * multiplicator); 
 +  }
 }; };
-  + 
-template <> struct fibonacci<2>+template<typename T> 
 +struct Square : public crtp<T, Square>
 { {
-  static const long value = 1;+  void square() 
 +  { 
 +    this->underlying().setValue(this->underlying().getValue() * this->underlying().getValue()); 
 +  }
 }; };
-  
-int main() 
-{ 
-  long i = fibonacci<70>::value; 
-  return 0; 
-} 
-</file> 
  
-gcc et clang génère 190392490709135 avec succès. 
  
-  * constexpr +class Sensitivity : public Scale<Sensitivity>, public Square<Sensitivity>
- +
-<file cpp fibonacci2.cc> +
-constexpr long fib(long n)+
 { {
-  if (n <= 2) { +public: 
-    return 1;+  double getValue() const 
 +  
 +    return value_;
   }   }
-  return fib(n - 1) + fib(n - 2); +  void setValue(double value) 
-}+  { 
 +    value_ = value
 +  } 
 +private: 
 +  double value_ = 0.; 
 +};
  
 int main() int main()
 { {
-  constexpr long i = fib(70);+  Sensitivity s; 
 + 
 +  s.setValue(10.); 
 +   
 +  s.scale(2.); 
   return 0;   return 0;
 } }
-</file>+</code>
  
-gcc arrive à calculer jusqu'à la valeur 35 et clang jusqu'à 26Au delà, il faut utiliser ''-fconstexpr-ops-limit='' (2^25 par défautpour gcc et ''-fconstexpr-backtrace-limit=80 -fconstexpr-steps=2147483647'' pour clang.+[[https://www.fluentcpp.com/2017/05/12/curiously-recurring-template-pattern/|The Curiously Recurring Template Pattern (CRTP)]] {{ :lang:cpp:class:the_curiously_recurring_template_pattern_crtp_-_fluent_c_2020-03-10_1_14_10_pm_.html |Archive du 12/05/2017 le 13/03/2020}}
  
-<note important>Le code est beaucoup moins optimisé dans cette version avec constexprIl 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. +[[https://www.fluentcpp.com/2017/05/16/what-the-crtp-brings-to-code/|What the Curiously Recurring Template Pattern can bring to your code]] {{ :lang:cpp:class:what_the_curiously_recurring_template_pattern_can_bring_to_your_code_-_fluent_c_2020-03-10_1_14_15_pm_.html |Archive du 16/05/2017 le 13/03/2020}}
-</note> +
-===Nombres premiers===+
  
-  * Template+[[https://www.fluentcpp.com/2017/05/19/crtp-helper/|An Implementation Helper For The Curiously Recurring Template Pattern]] {{ :lang:cpp:class:an_implementation_helper_for_the_curiously_recurring_template_pattern_-_fluent_c_2020-03-10_1_14_21_pm_.html |Archive du 19/07/2017 le 13/03/2020}}
  
-[[https://www.youtube.com/watch?v=EkdfiHs78DY|CppCon 2016: Peter Gottschling “How bad is Meta-Programming still today?"]]+[[https://www.fluentcpp.com/2018/05/22/how-to-transform-a-hierarchy-of-virtual-methods-into-a-crtp/|How to Turn a Hierarchy of Virtual Methods into a CRTP]] {{ :lang:cpp:class:how_to_turn_a_hierarchy_of_virtual_methods_into_a_crtp_-_fluent_c_2020-03-10_1_14_27_pm_.html |Archive du 22/08/2018 le 13/03/2020}}
  
-[[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}}+[[https://www.fluentcpp.com/2018/06/22/variadic-crtp-opt-in-for-class-features-at-compile-time/|Variadic CRTP: An Opt-in for Class Features, at Compile Time]] {{ :lang:cpp:class:variadic_crtp_an_opt-in_for_class_features_at_compile_time_-_fluent_c_2020-03-10_1_14_42_pm_.html |Archive du 22/06/2018 le 13/03/2020}}
  
-<file cpp prime.cc> +[[https://www.fluentcpp.com/2018/07/03/how-to-reduce-the-code-bloat-of-a-variadic-crtp/|How to Reduce the Code Bloat of a Variadic CRTP]] {{ :lang:cpp:class:how_to_reduce_the_code_bloat_of_a_variadic_crtp_-_fluent_c_2020-03-10_1_14_48_pm_.html |Archive du 03/07/2018 le 13/03/2020}}
-#include <type_traits>+
  
-template <int N> +[[https://www.fluentcpp.com/2018/06/26/variadic-crtp-packs-from-opt-in-skills-to-opt-in-skillsets/|Variadic CRTP PacksFrom Opt-in Skills to Opt-in Skillsets]] {{ :lang:cpp:class:variadic_crtp_packs_from_opt-in_skills_to_opt-in_skillsets_-_fluent_c_2020-03-10_1_14_56_pm_.html |Archive du 26/06/2018 le 13/03/2020}}
-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 { +[[https://www.fluentcpp.com/2018/08/28/removing-duplicates-crtp-base-classes/|Removing Duplicates in C++ CRTP Base Classes]] {{ :lang:cpp:class:removing_duplicates_in_c_crtp_base_classes_-_fluent_c_2020-03-10_1_15_02_pm_.html |Archive du 28/08/2018 le 13/03/2020}}
-  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 {};+====Mixin classes (similaire à CRTP mais sans inversion de l'héritage)====
  
-template <long x> struct max_prime_compare { +Ce pattern peut être utilisé par le pattern [[helloworld:design_pattern_decorator|décorateur]].
-  static long const tmp = Sqrt<x>::value, value = tmp % 2 == 0 ? tmp + 1 : tmp + 2; +
-};+
  
-template <long x, bool disablestruct check_odd +Le code optimisé généré par le compilateur est parfaitement identique à l'exemple précédent. 
-  static bool const value = is_prime_to_max_odd<x, max_prime_compare<x>::value>::value;+ 
 +<code cpp> 
 +class Sensitivity { 
 + public: 
 +  double getValue() const { return value_; } 
 +  void setValue(double value) { value_ = value; } 
 + 
 + private: 
 +  double value_ = 0.;
 }; };
  
-template <long x> struct check_odd <x, true> +template <typename T> 
-  static bool const value = false;+struct Scale : public T 
 +  void scale(double multiplicator) { 
 +    this->setValue(this->getValue() * multiplicator); 
 +  }
 }; };
  
-template <long x> struct is_prime +template <typename T> 
-  static bool const value = check_odd<x, x%2==0>::value;+struct Square : public T 
 +  void square() { this->setValue(this->getValue() * this->getValue())}
 }; };
  
-template <> struct is_prime <1: std::false_type {}; +class SensitivityEnhanced : public Scale<Square<Sensitivity>
-template <> struct is_prime <2> : std::true_type {};+{};
  
 int main() { int main() {
-  bool b49991 = is_prime<49991>::value+  SensitivityEnhanced s; 
-  bool b49992 = is_prime<49992>::value+ 
-  bool b49993 = is_prime<49993>::value;+  s.setValue(10.); 
 + 
 +  s.scale(2.); 
   return 0;   return 0;
 } }
 +</code>
 +
 +[[https://www.fluentcpp.com/2017/12/12/mixin-classes-yang-crtp/|Mixin Classes: The Yang of the CRTP]] {{ :lang:cpp:class:mixin_classes_the_yang_of_the_crtp_-_fluent_c_2020-03-10_1_28_44_pm_.html |Archive du 12/12/2017 le 10/03/2020}}
 +
 +L'utilisation des ''Mixin classes'' est mitigée. Certaines personnes pensent que c'est bien d'ajouter des fonctionnalités par l'héritage. D'autres (dont moi) pensent que si les nouvelles fonctionnalités ne sont pas en rapport avec la classe de base, l'utilisation de la composition est mieux.
 +
 +Pour Mixin : [[http://www.thinkbottomup.com.au/site/blog/C%20%20_Mixins_-_Reuse_through_inheritance_is_good|C++ Mixins - Reuse through inheritance is good... when done the right way]] {{ :lang:cpp:template:c_mixins_-_reuse_through_inheritance_is_good..._when_done_the_right_way_think_bottom_up_pty_ltd_2020-03-12_22_27_44_.html |Archive du 20/09/2011 le 12/03/2020}}
 +
 +Pour Composition : [[https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750|Mixins Are Dead. Long Live Composition]] {{ :lang:cpp:template:mixins_are_dead._long_live_composition_-_dan_abramov_-_medium_2020-03-12_22_58_13_.html |Archive du 13/03/2015 le 12/03/2020}}
 +
 +Pour mitigé :
 +
 +<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
 +    * Pas besoin d'avoir d'un coté la déclaration des classes (entête) et de l'autre l'implémentation.
 +    * Pour les templates, le compilateur va générer automatiquement le code pour chaque symbole pour chaque variante d'un template. En mettant le code dans l'entête, on est sûr de ne pas avoir des ''undefined reference to XXX'' en oubliant d'instancier un template extern.
 +    * Pas besoin d'utiliser des extern template.
 +  * Inconvénient
 +    * Les fichiers entête sont plus dur à lire pour trouver la liste des méthodes publiques. Sinon, il est possible de déclarer les méthodes dans la classe et de définir les méthodes inline en dessous de la classe.
 +    * Le compilateur va générer le code des fonctions dans chaque fichier source où le template est utilisé.
 +    * Le nombre de fichiers inclus dans les entêtes sera (sauf exception) plus important que si on avait laissé uniquement la déclaration de la classe dans le fichier entête. En effet, certains entrées ne sont peut-être nécessaire que pour l'implémentation et par pour le prototype des méthodes.
 +    * Le lieur va avoir de nombreux fichiers objets ayant les mêmes symboles de type Weak. Il va donc devoir faire beaucoup de nettoyage et le compilateur aura beaucoup travaillé pour rien. Pire, si le code de la méthode template est dépendant d'une macro qui varie entre plusieurs fichiers cpp, seules une des implémentations sera retenue puis généralisée.
 +
 +====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>;''
 +
 +[[https://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possible | Splitting templated C++ classes into .hpp/.cpp files--is it possible?]] {{ :lang:cpp:template:class_-_splitting_templated_c_classes_into_.hpp_.cpp_files--is_it_possible_-_stack_overflow_2019-08-27_08_55_46_.html |Archive du 27/08/2019}}
 +
 +<file cpp template.h>
 +template<class X>
 +class T
 +{
 +public:
 +   void fff();
 +};
 </file> </file>
  
-gcc et clang arrive à calculer à la compilation.+<file cpp template_impl.cc> 
 +#include "template.h"
  
-====Appeler la méthode généralisée depuis la méthode spécialisée==== +template<class X> 
-C'est impossible. Il faut que la méthode de base appelle une méthode spécifique.+void T<X>::fff() {}
  
-<code cpp> +// On instancie uniquement T<int(et donc T<int>::fff()). 
-template<typename T> +template class T<int>; 
-void baseF(T t{ ... }+</file>
  
 +<WRAP center round important 60%>
 +Si on décide de mettre le ''template class'' dans le même fichier que le ''.cc'', il est indispensable de le mettre tout en bas du fichier, après la définition de toutes les fonctions sinon les fonctions qui peuvent être ''inline'' ne seront pas conservées en symbole.
 +</WRAP>
 +
 +<file cpp main.cc>
 +#include "template.h"
 +
 +int main()
 +{
 +  T<int> tint;
 +  return tint.fff();
 +}
 +</file>
 +
 +  * Exemple avec une fonction template
 +
 +<file cpp file.h>
 +class A {
 +    template<typename U>
 +    void f();
 +};
 +</file>
 +
 +<file cpp file.cpp>
 +template<typename U> void A::f(){}
 +
 +// Instanciation pour U=int
 +template void A::f<int>();
 +
 +// Spécialisation pour U=double
 +template<> void A::f<double>(){}
 +</file>
 +
 +  * Exemple avec une fonction template dans une classe template
 +
 +<file cpp file.h>
 template<typename T> template<typename T>
-void F(T t) baseF<T>(t); }+class A { 
 +    template<typename U> 
 +    void f(U u); 
 +}
 +</file>
  
-template<> +<file cpp file.cpp> 
-void F<int>(int t) { baseF<int>(t); }+template<typename Ttemplate<typename U> void A<T>::f(U u){} 
 + 
 +template void A<int>::f(short u)
 +</file> 
 + 
 +  * Messages d'erreur 
 + 
 +''error: specialization of XXX after instantiation'' 
 + 
 +''error: explicit specialization of XXX after instantiation'' 
 + 
 +''error C2908: explicit specialization; XXX has already been instantiated'' 
 + 
 +Il ne faut pas utiliser une classe spécialisée avant qu'elle ne soit définie. 
 + 
 +<code cpp> 
 +template <typename T> 
 +class A {}; 
 + 
 +// Doit être défini après la spécialisation. 
 +A<int> a; 
 + 
 +template <> 
 +class A<int> {}; 
 +</code> 
 +====Afficher en string le type template==== 
 +<code cpp> 
 +typeid(T).name();
 </code> </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/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}} 
 + 
 +[[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}} 
 + 
 +[[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 471: 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.1583668880.txt.gz · Dernière modification : 2020/03/08 13:01 de root