lang:cpp:template
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
lang:cpp:template [2020/03/08 13:01] – [Boucles sur des types] : simplification de fold root | lang:cpp:template [2025/02/05 10:07] (Version actuelle) – [const Args...] : fix typo root | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ====Avantages/ | + | =====Restriction===== |
- | * Avantages | + | |
- | * Pas besoin d' | + | |
- | * 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' | + | |
- | * Pas besoin d' | + | |
- | * 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' | + | |
- | * 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 '' | + | |
- | [[https:// | + | Il n'est pas possible |
- | <file cpp template.h> | + | <code cpp> |
- | template<class X> | + | typename< |
- | class T | + | R fonction() |
{ | { | ||
- | public: | + | |
- | | + | } |
- | }; | + | </code> |
- | </file> | + | |
- | <file cpp template_impl.cc> | + | Il faut utiliser '' |
- | #include " | + | |
- | template<class X> | + | <code cpp> |
- | void T< | + | auto fonction() |
+ | { | ||
+ | return 1; | ||
+ | } | ||
+ | </ | ||
- | template class T< | + | ====const Args...==== |
- | </ | + | |
- | <note important> | + | Il faut éviter les '' |
- | <file cpp main.cc> | + | Sinon, cela impose l' |
- | #include " | + | |
- | extern template class T<int>(); | + | <code cpp> |
- | + | template<typename ...Args> | |
- | int main() | + | void f(const Args&& |
- | { | + | |
- | T<int> tint; | + | |
- | | + | |
- | } | + | |
- | </ | + | |
- | ====Afficher en string le type template==== | + | f(1.2); |
- | <code cpp> | + | |
- | typeid(T).name(); | + | |
</ | </ | ||
- | [[https:// | + | J'ai déjà eu des codes plus complexes où cela posait problème. |
- | [[https:// | + | =====Héritage===== |
+ | ====CRTP (Curiously recurring | ||
- | [[https:// | + | ===Principe=== |
+ | Le pattern CRTP fait de l' | ||
- | ====Décorateur==== | + | Principe du CRTP : on définit l' |
- | [[https:// | ||
- | |||
- | * implicit return type | ||
<code cpp> | <code cpp> | ||
- | # | + | template |
- | # | + | class CRTPBase |
+ | { | ||
+ | private: | ||
+ | T& impl() { return *static_cast<T*>(this); } | ||
+ | }; | ||
- | struct Action1 | + | class CRTPDerived : public CRTPBase< |
- | struct Action2 | + | { |
+ | }; | ||
+ | </ | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | ===Virtual sans CRTP=== | ||
- | class Classe | + | <code cpp> |
+ | class VirtualBase | ||
{ | { | ||
- | public: | + | |
- | | + | |
- | template< | + | |
- | auto go(Args ...args) | + | |
- | { | + | |
- | // | + | |
- | } | + | |
}; | }; | ||
- | // Action 1. | + | class VirtualDerived |
- | template<> | + | |
- | auto Classe:: | + | |
{ | { | ||
- | std::cout << " | + | public |
- | } | + | int m_counter; |
+ | public | ||
+ | VirtualDerived() | ||
+ | int tick(int n) { m_counter += n; return m_counter; } | ||
+ | }; | ||
- | // Action 2. | + | int test_virtual_methods |
- | template<> | + | |
- | auto Classe:: | + | |
{ | { | ||
- | std::cout << " | + | VirtualBase* pObj = static_cast< |
- | return static_cast< | + | for( int i = 0 ; i < test_run; i++ ) |
+ | { | ||
+ | for( int j = 0 ; j < test_run; j++ ) | ||
+ | | ||
+ | pObj-> | ||
+ | } | ||
+ | } | ||
+ | | ||
} | } | ||
- | template <class T> | + | int main (int argc, char** argv) |
- | class Decorator | + | |
{ | { | ||
- | public: | + | |
- | Decorator(std:: | + | } |
- | + | ||
- | template< | + | |
- | auto go(Args ...args) | + | |
- | { | + | |
- | std::cout << " | + | |
- | | + | |
- | } | + | |
- | private: | + | |
- | std:: | + | |
- | }; | + | |
</ | </ | ||
- | * explicit return type | + | ===CRTP sans virtual=== |
<code cpp> | <code cpp> | ||
- | # | + | template |
- | #include < | + | class CRTPBase |
- | + | ||
- | struct Action1 { using Type = void; }; | + | |
- | struct Action2 { using Type = char; }; | + | |
- | + | ||
- | class Classe | + | |
{ | { | ||
- | public: | + | |
- | | + | |
- | template< | + | |
- | T go(Args ...args) | + | |
{ | { | ||
- | // | + | return impl().tick(n); |
} | } | ||
+ | private: | ||
+ | T& impl() { return *static_cast< | ||
}; | }; | ||
- | // Action 1. | + | class CRTPDerived |
- | template<> | + | |
- | Action1:: | + | |
{ | { | ||
- | std::cout << " | + | public |
- | } | + | int m_counter; |
+ | public: | ||
+ | CRTPDerived() | ||
+ | int tick(int n) { m_counter += n; return m_counter; } | ||
+ | }; | ||
- | // Action 2. | + | int test_crtp |
- | template<> | + | |
- | Action2:: | + | |
{ | { | ||
- | std:: | + | CRTPBase<CRTPDerived> |
- | | + | for( int i = 0 ; i < test_run; i++ ) |
- | } | + | { |
- | + | | |
- | template | + | |
- | class Decorator | + | |
- | { | + | |
- | public: | + | |
- | | + | |
- | + | ||
- | template< | + | |
- | auto go(Args ...args) | + | |
{ | { | ||
- | std::cout << " | + | pObj->tick(j); |
- | return classe->template go<U, typename U::Type>(args...); | + | |
} | } | ||
- | private: | + | } |
- | | + | |
- | }; | + | } |
- | </ | + | |
- | * main.cc | + | int main (int argc, char** argv) |
- | + | ||
- | <code cpp> | + | |
- | int main() | + | |
{ | { | ||
- | Decorator< | + | return test_crtp(2000); |
- | c.go< | + | |
- | c.go< | + | |
} | } | ||
</ | </ | ||
- | ====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 '' |
- | ===Exemple simple=== | + | |
- | <code cpp> | + | |
- | #include < | + | |
- | template | + | J'ai essayé de résoudre ce problème en utilisant un '' |
- | constexpr T square(T x) { | + | |
- | return x*x; | + | |
- | } | + | |
- | int main() { | + | ===Benchmark=== |
- | printf(" | + | |
- | // 11.560000 9 | + | |
- | } | + | |
- | </ | + | |
- | ===Fibonacci=== | + | ^ Performance |
+ | | Virtuelle | ||
+ | | CRTP | 3, | ||
- | * 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 | + | |
+ | clang : '' | ||
+ | |||
+ | gcc : '' | ||
+ | |||
+ | Visual Studio : '' | ||
+ | |||
+ | Pour avoir un parent différent, l' | ||
+ | |||
+ | <code cpp> | ||
+ | template< | ||
+ | struct | ||
{ | { | ||
- | | + | |
+ | { | ||
+ | return static_cast<T&>(*this); | ||
+ | } | ||
+ | |||
+ | T const& underlying() const | ||
+ | { | ||
+ | return static_cast<T const&>(*this); | ||
+ | } | ||
}; | }; | ||
- | + | ||
- | template <> struct | + | template< |
+ | struct | ||
{ | { | ||
- | | + | |
+ | { | ||
+ | this-> | ||
+ | } | ||
}; | }; | ||
- | + | ||
- | template <> struct | + | template< |
+ | struct | ||
{ | { | ||
- | | + | |
+ | { | ||
+ | this-> | ||
+ | } | ||
}; | }; | ||
- | |||
- | int main() | ||
- | { | ||
- | long i = fibonacci< | ||
- | return 0; | ||
- | } | ||
- | </ | ||
- | gcc et clang génère 190392490709135 avec succès. | ||
- | * constexpr | + | class Sensitivity : public Scale<Sensitivity>, |
- | + | ||
- | <file cpp fibonacci2.cc> | + | |
- | constexpr long fib(long n) | + | |
{ | { | ||
- | if (n <= 2) { | + | public: |
- | return | + | double getValue() const |
+ | | ||
+ | return | ||
} | } | ||
- | | + | |
- | } | + | { |
+ | value_ = value; | ||
+ | } | ||
+ | private: | ||
+ | double value_ = 0.; | ||
+ | }; | ||
int main() | int main() | ||
{ | { | ||
- | | + | |
+ | |||
+ | s.setValue(10.); | ||
+ | |||
+ | s.scale(2.); | ||
return 0; | return 0; | ||
} | } | ||
- | </file> | + | </code> |
- | gcc arrive à calculer jusqu' | + | [[https:// |
- | <note important> | + | [[https:// |
- | </note> | + | |
- | ===Nombres premiers=== | + | |
- | * Template | + | [[https:// |
- | [[https:// | + | [[https:// |
- | [[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/ |
- | < | + | [[https:// |
- | #include < | + | |
- | template <int N> | + | [[https:// |
- | struct Sqrt { | + | |
- | template <int lo, int hi> | + | |
- | struct Helper { | + | |
- | static const int mid = (lo + hi + 1) / 2; | + | |
- | static const int value = std:: | + | |
- | }; | + | |
- | 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:// |
- | static bool const value = x % max_odd != 0 && | + | |
- | is_prime_to_max_odd< | + | |
- | }; | + | |
- | template <long x> struct is_prime_to_max_odd< | + | ====Mixin classes (similaire à CRTP mais sans inversion de l' |
- | 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< | + | |
- | }; | + | |
- | template | + | Le code optimisé généré par le compilateur est parfaitement identique à l' |
- | | + | |
+ | <code cpp> | ||
+ | class Sensitivity | ||
+ | | ||
+ | | ||
+ | void setValue(double | ||
+ | |||
+ | | ||
+ | double value_ = 0.; | ||
}; | }; | ||
- | template <long x> struct | + | template <typename T> |
- | | + | struct |
+ | | ||
+ | this-> | ||
+ | } | ||
}; | }; | ||
- | template <long x> struct | + | template <typename T> |
- | | + | struct |
+ | | ||
}; | }; | ||
- | template | + | class SensitivityEnhanced : public Scale<Square<Sensitivity> |
- | template <> struct is_prime <2> : std:: | + | {}; |
int main() { | int main() { | ||
- | | + | |
- | | + | |
- | | + | |
+ | |||
+ | | ||
return 0; | return 0; | ||
} | } | ||
+ | </ | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | L' | ||
+ | |||
+ | Pour Mixin : [[http:// | ||
+ | |||
+ | Pour Composition : [[https:// | ||
+ | |||
+ | Pour mitigé : | ||
+ | |||
+ | < | ||
+ | |||
+ | =====Divers===== | ||
+ | ====Avantages/ | ||
+ | * Avantages | ||
+ | * Pas besoin d' | ||
+ | * 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' | ||
+ | * Pas besoin d' | ||
+ | * 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' | ||
+ | * 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 '' | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | <file cpp template.h> | ||
+ | template< | ||
+ | class T | ||
+ | { | ||
+ | public: | ||
+ | void fff(); | ||
+ | }; | ||
</ | </ | ||
- | gcc et clang arrive à calculer à la compilation. | + | <file cpp template_impl.cc> |
+ | #include " | ||
- | ====Appeler la méthode généralisée depuis la méthode spécialisée==== | + | template< |
- | C'est impossible. Il faut que la méthode de base appelle une méthode spécifique. | + | void T< |
- | <code cpp> | + | // On instancie uniquement T<int> (et donc T<int>::fff()). |
- | template< | + | template class T< |
- | void baseF(T t) { ... } | + | </ |
+ | <WRAP center round important 60%> | ||
+ | Si on décide de mettre le '' | ||
+ | </ | ||
+ | |||
+ | <file cpp main.cc> | ||
+ | #include " | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | T< | ||
+ | return tint.fff(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * Exemple avec une fonction template | ||
+ | |||
+ | <file cpp file.h> | ||
+ | class A { | ||
+ | template< | ||
+ | void f(); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | <file cpp file.cpp> | ||
+ | template< | ||
+ | |||
+ | // Instanciation pour U=int | ||
+ | template void A:: | ||
+ | |||
+ | // Spécialisation pour U=double | ||
+ | template<> | ||
+ | </ | ||
+ | |||
+ | * Exemple avec une fonction template dans une classe template | ||
+ | |||
+ | <file cpp file.h> | ||
template< | template< | ||
- | void F(T t) { baseF<T>(t); } | + | class A { |
+ | template<typename U> | ||
+ | void f(U u); | ||
+ | }; | ||
+ | </ | ||
- | template<> | + | <file cpp file.cpp> |
- | void F< | + | template< |
+ | |||
+ | template | ||
+ | </ | ||
+ | |||
+ | * Messages d' | ||
+ | |||
+ | '' | ||
+ | |||
+ | '' | ||
+ | |||
+ | '' | ||
+ | |||
+ | Il ne faut pas utiliser une classe spécialisée avant qu' | ||
+ | |||
+ | <code cpp> | ||
+ | template < | ||
+ | class A {}; | ||
+ | |||
+ | // Doit être défini après la spécialisation. | ||
+ | A< | ||
+ | |||
+ | template <> | ||
+ | class A< | ||
+ | </ | ||
+ | ====Afficher en string le type template==== | ||
+ | <code cpp> | ||
+ | typeid(T).name(); | ||
</ | </ | ||
- | [[https:// | + | [[https:// |
+ | |||
+ | [[https:// | ||
+ | |||
+ | [[https:// | ||
====class ou typename==== | ====class ou typename==== | ||
Ligne 471: | Ligne 563: | ||
AA2< | AA2< | ||
} | } | ||
+ | </ | ||
+ | |||
+ | ====Sérialisation==== | ||
+ | |||
+ | Compter le nombre de champ d'une classe. | ||
+ | |||
+ | <code cpp> | ||
+ | #include < | ||
+ | |||
+ | struct UniversalType { | ||
+ | template < | ||
+ | operator T(); // no definition required | ||
+ | }; | ||
+ | |||
+ | template < | ||
+ | consteval auto MemberCounter(auto... c0) { | ||
+ | if constexpr (requires { T{{A0{}}..., | ||
+ | return MemberCounter< | ||
+ | else if constexpr ( | ||
+ | requires { | ||
+ | T{{A0{}}..., | ||
+ | } || | ||
+ | requires { | ||
+ | T{{A0{}}..., | ||
+ | }) | ||
+ | return MemberCounter< | ||
+ | return sizeof...(A0) + sizeof...(c0); | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | using TestType = struct { | ||
+ | int x[3]; | ||
+ | float y; | ||
+ | char z; | ||
+ | }; | ||
+ | auto [a, b, c] = TestType{}; | ||
+ | std::cout << MemberCounter< | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====Erreurs==== | ||
+ | |||
+ | * Il manque un mot clé '' | ||
+ | |||
+ | Il faut parfois rajouter le mot clé '' | ||
+ | |||
+ | <code cpp> | ||
+ | error: ' | ||
+ | return T::template f< | ||
+ | | ||
+ | </ | ||
+ | |||
+ | <code cpp> | ||
+ | return T::template f< | ||
+ | </ | ||
+ | |||
+ | * Il manque un mot clé '' | ||
+ | |||
+ | <code cpp> | ||
+ | error: expected ';' | ||
+ | retval-> | ||
+ | ^ | ||
+ | ; | ||
+ | </ | ||
+ | |||
+ | ou encore | ||
+ | |||
+ | <code cpp> | ||
+ | error: expression contains unexpanded parameter pack ' | ||
+ | retval-> | ||
+ | ^ ~ | ||
+ | </ | ||
+ | |||
+ | ou encore | ||
+ | |||
+ | <code cpp> | ||
+ | error: expected primary-expression before ' | ||
+ | return this-> | ||
+ | ^ | ||
+ | </ | ||
+ | |||
+ | Solution: | ||
+ | |||
+ | <code cpp> | ||
+ | retval-> | ||
+ | </ | ||
+ | |||
+ | ===Mapping d'un type vers un autre via une map=== | ||
+ | |||
+ | https:// | ||
+ | |||
+ | < | ||
+ | using my_map = type_map< | ||
+ | pair< | ||
+ | pair< | ||
+ | pair< | ||
+ | >; | ||
+ | |||
+ | static_assert(std:: | ||
+ | static_assert(std:: | ||
+ | static_assert(std:: | ||
+ | </ | ||
+ | |||
+ | <code cpp> | ||
+ | template < | ||
+ | struct type_tag | ||
+ | { | ||
+ | using type = T; | ||
+ | }; | ||
+ | |||
+ | template < | ||
+ | struct pair | ||
+ | { | ||
+ | using first_type = K; | ||
+ | using second_type = V; | ||
+ | }; | ||
+ | |||
+ | template < | ||
+ | struct element | ||
+ | { | ||
+ | static auto value(type_tag< | ||
+ | -> type_tag< | ||
+ | }; | ||
+ | |||
+ | template < | ||
+ | struct type_map : element< | ||
+ | { | ||
+ | using element< | ||
+ | |||
+ | template < | ||
+ | using find = typename decltype(type_map:: | ||
+ | }; | ||
</ | </ |
lang/cpp/template.1583668880.txt.gz · Dernière modification : 2020/03/08 13:01 de root