C++ Templates & Generic Programming

890288c65d45a8e6978917006e3d3bc7?s=47 Dan Chen
October 27, 2015

C++ Templates & Generic Programming

October 27, 2015. “Effective C++ 3/e — Chapter 7: Templates & Generic Programming” at Trend Micro internal sharing session.

Target audience: Elementary and intermediate C++ developers.

All copyrights of the excerpts belong to their respective owners.

890288c65d45a8e6978917006e3d3bc7?s=128

Dan Chen

October 27, 2015
Tweet

Transcript

  1. 1.
  2. 2.

    Foreword Another world in C ++ wonderland (請自備暈車藥和嘔吐袋 :P )

    Template mechanism is Turing - complete Template Metaprogramming (TMP ) = programming C ++ compiler (P .236) If you think this is cooler than ice cream , you ' ve got the makings of a template metaprogrammer . If the templates and specializations and recursive instantiations and enum hacks and the need to type things like F a c t o r i a l < n - 1 > : : v a l u e make your skin crawl , well , you ' re a pretty normal C ++ programmer . Generic Programming = paradigm for developing reusable libraries Standard Template Library (STL ) 2 / 52
  3. 3.

    Recap — C++ Templates Definition & Instantiation & Specialization /

    / [ f u n c t i o n t e m p l a t e ] = ~ e q u a t i o n t h a t c o m p i l e r s n e e d t o s o l v e t e m p l a t e < t y p e n a m e T , t y p e n a m e U > i n t s u m ( T a , U b ) { … } / / [ c l a s s t e m p l a t e ] = ~ e q u a t i o n t h a t y o u p r o v i d e a s o l u t i o n l a t e r t e m p l a t e < t y p e n a m e T , t y p e n a m e U > c l a s s F o o { … } ; / / [ i n s t a n t i a t i o n ] = ~ o n e o f p o s s i b l e s o l u t i o n s t o t h e e q u a t i o n s u m ( 1 , 2 ) ; / / o k , T = i n t , U = i n t s u m ( " H e l l o " , " w o r l d " ) ; / / c o m p i l e e r r o r ! F o o < d o u b l e , c h a r > f o o ; / / o k , T = d o u b l e , U = c h a r F o o f o o ; / / c o m p i l e e r r o r ! / / [ f u l l s p e c i a l i z a t i o n ] = ~ e q u a t i o n b e h a v e s s p e c i a l l y o n s p e c i f i c s o l u t i o n t e m p l a t e < > i n t s u m < > ( i n t a , i n t b ) { … } i n t s u m ( i n t a , i n t b ) { … } / / o v e r l o a d i n g , n o t a s p e c i a l i z a t i o n t e m p l a t e < > c l a s s F o o < i n t , i n t > { … } ; / / [ p a r t i a l s p e c i a l i z a t i o n ] = ~ e q u a t i o n b e h a v e s s p e c i a l l y o n p a r t i a l l y s p e c i f i c s o l . t e m p l a t e < t y p e n a m e T > i n t s u m < T , i n t > ( T a , i n t b ) { … } / / c o m p i l e e r r o r ! t e m p l a t e < t y p e n a m e U > c l a s s F o o < i n t , U > { … } ; t e m p l a t e < t y p e n a m e T , t y p e n a m e U > c l a s s F o o < T * , U > { … } ; 3 / 52
  4. 4.

    Practice — Function Template Instantiation & Overload Resolution / /

    ( a ) t e m p l a t e < t y p e n a m e T , t y p e n a m e U > i n t c a l c ( T a , U b ) { r e t u r n ( a + b ) ; } / / ( b ) t e m p l a t e < > i n t c a l c < > ( i n t a , i n t b ) { r e t u r n ( a * b ) ; } / / ( c ) t e m p l a t e < t y p e n a m e T > i n t c a l c ( T a , i n t b ) { r e t u r n ( a - b ) ; } / / ( d ) i n t c a l c ( i n t a , i n t b ) { r e t u r n ( a < < b ) ; } c a l c ( 4 . 0 , 2 . 0 ) ; / / d o u b l e , d o u b l e - - - - > ( a ) 6 c a l c ( 4 . 0 , 2 ) ; / / d o u b l e , i n t - - - - - - - > ( c ) 2 c a l c ( 4 , 2 ) ; / / i n t , i n t - - - - - - - - - - > ( d ) 1 6 c a l c ( 4 U , 2 ) ; / / u i n t , i n t - - - - - - - - - > ( c ) 2 c a l c ( 4 U , 2 U ) ; / / u i n t , u i n t - - - - - - - - > ( a ) 6 c a l c < i n t , i n t > ( 4 , 2 ) ; / / i n t , i n t ( t m p l ) - - - > ( b ) 8 c a l c < i n t , i n t > ( 4 U , 2 U ) ; / / u i n t , u i n t ( t m p l ) - > ( b ) 8 c a l c < > ( 4 , 2 ) ; / / i n t , i n t ( t m p l ) - - - > ( c ) 2 c a l c < > ( 4 U , 2 U ) ; / / u i n t , u i n t ( t m p l ) - > ( a ) 6 c a l c < i n t > ( 4 , 2 ) ; / / i n t , i n t ( t m p l ) - - - > ( c ) 2 c a l c < i n t > ( 4 , 2 U ) ; / / i n t , u i n t ( t m p l ) - - > ( a ) 6 C ++ compilers prefer the easiest path Refer to Appendix A & Appendix B of this document 4 / 52
  5. 5.

    MSDN – Differences Between C ++ Templates and C #

    Generics C # generics do not provide the same amount of flexibility as C ++ templates . For example , it is not possible to call arithmetic operators in a C # generic class , although it is possible to call user defined operators . C # does not allow non -type template parameters , such as t e m p l a t e C < i n t i > { } . C # does not support explicit specialization ; that is , a custom implementation of a template for a specific type . C # does not support partial specialization : a custom implementation for a subset of the type arguments . C # does not allow the type parameter to be used as the base class for the generic type . C # does not allow type parameters to have default types . In C #, a generic type parameter cannot itself be a generic , although constructed types can be used as generics . C ++ does allow template parameters . C ++ allows code that might not be valid for all type parameters in the template , which is then checked for the specific type used as the type parameter . C # requires code in a class to be written in such a way that it will work with any type that satisfies the constraints . 5 / 52
  6. 6.

    Stack Overflow – What are the main differences between C

    ++ templates and Java generics ? In java the compiled code removes all references to the generic type and adds casts where necessary . This is called type erasure and lets you do L i s t < S t r i n g > l i s t ; then ( ( L i s t ) l i s t ) . a d d ( n e w O b j e c t ( ) ) ; which will only throw an error when you try to get the value as a string . All java generics actually are is syntactic sugar with extra compile -time type checking . In C ++ when you use a template the compiler will emit the template code again after replacing the generic parameter in it with the type you used . This is more powerful in several ways but can lead to bloated executables . Dr . Dobb ' s – Java Generics and C ++ Templates Wikipedia – Comparison of Java and C ++ 6 / 52
  7. 8.

    Item 41 Understand implicit interfaces and compile -time polymorphism 了解隱式介面和

    編譯期多型 Explicit interface = function signatures Function names Parameter types Return types Implicit interface = valid expressions T t e m p ( w ) ; t e m p . n o r m a l i z e ( ) ; w . s i z e ( ) > 1 0 ; Runtime polymorphism Dynamic binding of virtual function calls Compile -time polymorphism Which of a set of overloaded functions should be called 8 / 52
  8. 9.

    Item 41 Understand implicit interfaces and compile -time polymorphism 了解隱式介面和

    編譯期多型 From function to function template v o i d d o p r o c e s s i n g ( W i d g e t & w ) { i f ( w . s i z e ( ) > 0 & & w ! = s o m e N a s t y W i d g e t ) { … } } t e m p l a t e < t y p e n a m e T > v o i d d o P r o c e s s i n g ( T & w ) { i f ( w . s i z e ( ) > 0 & & w ! = s o m e N a s t y W i d g e t ) { … } } Implicit interface of T Member function s i z e that returns an integral value Support o p e r a t o r ! = that compares two T objects (assume s o m e N a s t y W i d g e t is also a T) But … s i z e could be inherited from base class What s i z e returns even need not have o p e r a t o r > defined inside Implicit type conversion from X to Y for o p e r a t o r > 9 / 52
  9. 10.

    Item 41 Understand implicit interfaces and compile -time polymorphism 了解隱式介面和

    編譯期多型 Things to Remember Both classes and templates support interfaces and polymorphisms . For classes , interfaces are explicit and centered on function signatures . Polymorphism occurs at runtime through virtual functions . For template parameters , interfaces are implicit and based on valid expressions . Polymorphism occurs during compilation through template instantiation and function overloading resolution . 10 / 52
  10. 11.

    Item 42 Understand the two meanings of t y p

    e n a m e 了解 t y p e n a m e 的雙重意義 11 / 52
  11. 12.

    Item 42 Understand the two meanings of t y p

    e n a m e 了解 t y p e n a m e 的雙 重意義 When declaring a template type parameter , c l a s s and t y p e n a m e are equivalent t e m p l a t e < c l a s s T > c l a s s W i d g e t ; t e m p l a t e < t y p e n a m e T > c l a s s W i d g e t ; However , sometimes you must use t y p e n a m e t e m p l a t e < t y p e n a m e C > v o i d p r i n t 2 n d ( c o n s t C & c o n t a i n e r ) { i f ( c o n t a i n e r . s i z e ( ) > = 2 ) { C : : i t e r a t o r i t ( c o n t a i n e r . b e g i n ( ) ) ; / / c o m p i l e e r r o r ! + + i t ; s t d : : c o u t < < ( * i t ) ; } } Names in a template that are dependent on a template parameter are called dependent names C ++ compilers have no idea what C : : i t e r a t o r is — what if it 's a static data member ? t y p e n a m e indicates “it 's a type name .” t y p e n a m e C : : i t e r a t o r i t ( c o n t a i n e r . b e g i n ( ) ) ; 12 / 52
  12. 13.

    Item 42 Understand the two meanings of t y p

    e n a m e 了解 t y p e n a m e 的雙 重意義 However (again ), “t y p e n a m e must precede nested dependent type names ” has exception Base class list Member initialization list t e m p l a t e < t y p e n a m e T > c l a s s D e r i v e d : p u b l i c / * t y p e n a m e * / B a s e < T > : : N e s t e d { p u b l i c : e x p l i c i t D e r i v e d ( i n t x ) : / * t y p e n a m e * / B a s e < T > : : N e s t e d ( x ) { t y p e n a m e B a s e < T > : : N e s t e d t e m p ; … } } Aliasing t y p e n a m e s — t y p e d e f and u s i n g (since C ++11) t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < I t e r T > : : v a l u e _ t y p e t e m p ( * i t e r ) ; t y p e d e f t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < I t e r T > : : v a l u e _ t y p e v a l u e _ t y p e ; v a l u e _ t y p e t e m p ( * i t e r ) ; / / a v a i l a b l e s i n c e C + + 1 1 u s i n g v a l u e _ t y p e = t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < I t e r T > : : v a l u e _ t y p e ; v a l u e _ t y p e t e m p ( * i t e r ) ; 13 / 52
  13. 14.

    Item 42 Understand the two meanings of t y p

    e n a m e 了解 t y p e n a m e 的雙 重意義 Things to Remember When declaring template parameters , c l a s s and t y p e n a m e are interchangable . Use t y p e n a m e to identify nested dependent type names , except in base class lists or as a base class identifier in a member initialization list . 14 / 52
  14. 15.

    Item 43 Know how to access names in templatized base

    classes 學習處理模板化基礎類別內的名稱 15 / 52
  15. 16.

    Item 43 Know how to access names in templatized base

    classes 學習處理 模板化基礎類別 內的名稱 c l a s s C o m p a n y A { p u b l i c : … v o i d s e n d C l e a r t e x t ( s t d : : s t r i n g c o n s t & m s g ) ; v o i d s e n d E n c r y p t e d ( s t d : : s t r i n g c o n s t & m s g ) ; … } ; c l a s s C o m p a n y B { p u b l i c : … v o i d s e n d C l e a r t e x t ( s t d : : s t r i n g c o n s t & m s g ) ; v o i d s e n d E n c r y p t e d ( s t d : : s t r i n g c o n s t & m s g ) ; … } ; c l a s s M s g I n f o { … } ; t e m p l a t e < t y p e n a m e C o m p a n y > c l a s s M s g S e n d e r { p u b l i c : … v o i d s e n d C l e a r ( M s g I n f o c o n s t & i n f o ) { s t d : : s t r i n g m s g ( i n f o . m ( ) ) ; C o m p a n y c ; c . s e n d C l e a r t e x t ( m s g ) ; } } ; t e m p l a t e < t y p e n a m e C o m p a n y > c l a s s L o g g i n g M s g S e n d e r : p u b l i c M s g S e n d e r < C o m p a n y > { p u b l i c : … v o i d S e n d C l e a r M s g ( M s g I n f o c o n s t & i n f o ) { l o g g e r . s t a r t ( i n f o ) ; s e n d C l e a r ( i n f o ) ; / / c o m p i l e e r r o r ! l o g g e r . f i n i s h ( i n f o ) ; } … } ; S e n d C l e a r M s g uses a different name instead of s e n d C l e a r to side -steps the issue of hiding inherited names (Item 33), and the problem inherent in redefing an inherited non - virtual function (Item 36). 16 / 52
  16. 17.

    Item 43 Know how to access names in templatized base

    classes 學習處理 模板化基礎類別 內的名稱 M s g S e n d e r < C o m p a n y > remains unknown until L o g g i n g M s g S e n d e r is instantiated . Therefore , compiler has no idea if M s g S e n d e r < C o m p a n y > has a s e n d C l e a r function . Huh ? t y p e n a m e < > = total template specialization c l a s s C o m p a n y Z { … / / o f f e r s n o ` s e n d C l e a r t e x t ` f u n c t i o n v o i d s e n d E n c r y p t e d ( s t d : : s t r i n g c o n s t & m s g ) ; … } ; t e m p l a t e < > c l a s s M s g S e n d e r < C o m p a n y Z > { … / / ` s e n d C l e a r ` i s o m i t t e d v o i d s e n d S e c r e t ( M s g I n f o c o n s t & i n f o ) ; … } ; C ++ recognizes the base class template (M s g S e n d e r < C o m p a n y > ) may be specialized and that such specialization may not offer the same interface as the general template . As a result , it generally refuses to look in templatized base classes for inherited names . 17 / 52
  17. 18.

    Item 43 Know how to access names in templatized base

    classes 學習處理 模板化基礎類別 內的名稱 When we cross from Object -Oriented C ++ to Template C ++ (Item 1), inheritance somehow stops working :P Disable C ++'s “don ' t look in templatized base classes ” behavior 1. Preface calls to base class functions with t h i s - > t h i s - > s e n d C l e a r ( … ) ; 2. Employ a u s i n g declaration u s i n g M s g S e n d e r < C o m p a n y > : : s e n d C l e a r ; s e n d C l e a r ( … ) ; 3. Explicitly specify the function namespace (disable virtual function mechanism ) M s g S e n d e r < C o m p a n y > : : s e n d C l e a r ( … ) ; Promising compiler that any subsequent specialization of the base class template will support the interface C ++'s policy is to prefer early diagnoses , and that 's why it assumes it knows nothing about the content of base classes when those classes are instantiated from templates . 18 / 52
  18. 19.

    Item 43 Know how to access names in templatized base

    classes 學習處理 模板化基礎類別 內的名稱 Things to Remember In derived classes templates , refer to names in base class templates via a t h i s - > prefix , via u s i n g declarations , or via an explicit base class qualification (least desirable way ). 19 / 52
  19. 21.

    Item 44 Factor parameter - independent code out of templates

    將與參數無關的 程式碼抽離 templates If you 're not careful , using templates can lead to code bloat — binaries with replicated code , data , or both . Commonality and variability analysis ( 共性和變性分析) In non -template code , replication is explicit : you can see that there 's duplication between two functions or two classes . In template code , replication is implicit : there 's only one copy of the template source code , so you have to train yourself to sense the replication that may take place when a template is instantiated multiple times . / / N i s a " n o n - t y p e p a r a m e t e r " t e m p l a t e < t y p e n a m e T , s t d : : s i z e _ t N > c l a s s S q u a r e M a t r i x { p u b l i c : … v o i d i n v e r t ( ) ; } ; S q u a r e M a t r i x < d o u b l e , 5 > s m 1 ; s m 1 . i n v e r t ( ) ; S q u a r e M a t r i x < d o u b l e , 1 0 > s m 2 ; s m 2 . i n v e r t ( ) ; 21 / 52
  20. 22.

    Item 44 Factor parameter - independent code out of templates

    將與參數無關的 程式碼抽離 templates Possible approach : factor the i n v e r t ( ) function to a common base class . t e m p l a t e < t y p e n a m e T > c l a s s S q u a r e M a t r i x B a s e { p r o t e c t e d : … v o i d i n v e r t ( s t d : : s i z e _ t m a t r i x S i z e ) ; } ; t e m p l a t e < t y p e n a m e T , s t d : : s i z e _ t N > c l a s s S q u a r e M a t r i x : p r i v a t e S q u a r e M a t r i x B a s e < T > { p r i v a t e : u s i n g S q u a r e M a t r i x B a s e < T > : : i n v e r t ; / / r e f . I t e m 3 3 a n d I t e m 4 3 p u b l i c : … v o i d i n v e r t ( ) { i n v e r t ( N ) ; } ; / / i n l i n e c a l l t o b a s e ` i n v e r t ( ) ` } ; It 's not a conceptual is - a relationship between S q u a r e M a t r i x and S q u a r e M a t r i x B a s e (Item 39). Problem : How does S q u a r e M a t r i x B a s e : : i n v e r t know what data to operate on ? Presumably only the derived class knows that . How does the derived class communicate that to the base class so that the base class can do the inversion ? 22 / 52
  21. 23.

    Item 44 Factor parameter - independent code out of templates

    將與參數無關的 程式碼抽離 templates t e m p l a t e < t y p e n a m e T > c l a s s S q u a r e M a t r i x B a s e { p r o t e c t e d : S q u a r e M a t r i x B a s e ( s t d : : s i z e _ t N , T * p M e m ) : s i z e ( n ) , p D a t a ( p M e m ) { } v o i d s e t D a t a P t r ( T * p t r ) { p D a t a = p t r ; } v o i d i n v e r t ( ) ; p r i v a t e : s t d : : s i z e _ t s i z e ; T * p D a t a ; } ; t e m p l a t e < t y p e n a m e T , s t d : : s i z e _ t N > c l a s s S q u a r e M a t r i x L o c a l : p r i v a t e S q u a r e M a t r i x B a s e < T > { p u b l i c : S q u a r e M a t r i x ( ) : S q u a r e M a t r i x B a s e < T > ( N , d a t a ) { } … p r i v a t e : T d a t a [ N * N ] ; } ; t e m p l a t e < t y p e n a m e T , s t d : : s i z e _ t N > c l a s s S q u a r e M a t r i x H e a p : p r i v a t e S q u a r e M a t r i x B a s e < T > { p u b l i c : S q u a r e M a t r i x ( ) : S q u a r e M a t r i x B a s e < T > ( N , N U L L ) , p D a t a ( n e w T [ N * N ] ) { t h i s - > S e t D a t a P t r ( p D a t a . g e t ( ) ) ; } … p r i v a t e : b o o s t : : s c o p e d _ a r r a y < T > p D a t a ; / / r e f . I t e m 1 3 } ; 23 / 52
  22. 24.

    Item 44 Factor parameter - independent code out of templates

    將與參數無關的 程式碼抽離 templates The versions of i n v e r t with the matrix size hardwired into them are likely to generate better code than the shared version where the size is passed as a function parameter or is stored in the object . On the other hand , having only one version of i n v e r t for multiple matrix sizes decreases the size of the executable , and that should reduce the program 's working set size and improve locality of reference in the instruction cache . This Item has discussed only bloat due to non -type template parameters , but the type parameters can lead to bloat , too . For example , on many platforms , i n t and l o n g have the same binary representation , so the member functions for , say , v e c t o r < i n t > and v e c t o r < l o n g > would likely be identical — the very definition of bloat . Some linkers will merge identical function implementations , but some will not . Similarly , all pointer types have the same binary representation , so templates holding pointer types (e .g ., l i s t < i n t * > , l i s t < c o n s t i n t * > , l i s t < S q u a r e M a t r i x < l o n g , 3 > * > )often be able to use a single underlying implementation for each member function . 24 / 52
  23. 25.

    Item 44 Factor parameter - independent code out of templates

    將與參數無關的 程式碼抽離 templates Things to Remember Templates generate multiple classes and multiple functions , so many template code not dependent on a template parameter causes bloat . Bloat due to non -type template parameters can often be eliminated by replacing template parameters with function parameters or class data members . Bloat due to type parameters can be reduced by sharing implementations for instantiations types with identical binary representations . 25 / 52
  24. 26.

    Item 45 Use member function templates to accept “all compatible

    types” 利用成員函式模板接受所有相容型別 26 / 52
  25. 27.

    Item 45 Use member function templates to accept “all compatible

    types ” 利用成員函式模板 接受所有相容型別 c l a s s T o p { … } ; c l a s s M i d d l e : p u b l i c T o p { … } ; c l a s s B o t t o m : p u b l i c M i d d l e { … } ; t e m p l a t e < t y p e n a m e T > c l a s s S m a r t P t r { p u b l i c : e x p l i c i t S m a r t P t r ( T * r e a l P t r ) ; … } ; S m a r t P t r < T o p > p t r 1 = S m a r t P t r < M i d d l e > ( n e w M i d d l e ) ; / / c o m p i l e e r r o r ! S m a r t P t r < T o p > p t r 2 = S m a r t P t r < B o t t o m > ( n e w B o t t o m ) ; / / c o m p i l e e r r o r ! S m a r t P t r < c o n s t T o p > p c t 2 = p t r 1 ; / / c o m p i l e e r r o r ! There is no inherent relationship among different instantiations of the same template (e .g , S m a r t P t r < … >). What we need is a constructor template which enables implicit conversion from a U * pointer to a T * pointer . t e m p l a t e < t y p e n a m e T > c l a s s S m a r t P t r { p u b l i c : t e m p l a t e < t y p e n a m e U > S m a r t P t r ( c o n s t S m a r t P t r < U > & t h e O t h e r ) : h e l d P t r ( t h e O t h e r . g e t ( ) ) { … } … } ; 27 / 52
  26. 28.

    Item 45 Use member function templates to accept “all compatible

    types ” 利用成員函式模板 接受所有相容型別 Member function templates don ' t change the rules of the language , and the rules state that if a copy constructor is needed and you don ' t declare one , one will be generated for your automatically . If you want to control all aspects of copy construction , you must declare both a generalized copy constructor (template ), as well as the “normal ” copy constructor . / / e x c e r p t f r o m s t d : : t r 1 : : s h a r e d _ p t r < > t e m p l a t e < c l a s s T > c l a s s s h a r e d _ p t r { s h a r e d _ p t r ( s h a r e d _ p t r c o n s t & r ) ; t e m p l a t e < c l a s s Y > s h a r e d _ p t r ( s h a r e d _ p t r < Y > c o n s t & r ) ; s h a r e d _ p t r & o p e r a t o r = ( s h a r e d _ p t r c o n s t & r ) t e m p l a t e < c l a s s Y > s h a r e d _ p t r & o p e r a t o r = ( s h a r e d _ p t r < Y > c o n s t & r ) ; … } ; Note : s h a r e d _ p t r inside c l a s s s h a r e _ p t r { … } is a shortcut of s h a r e d _ p t r < T > . 28 / 52
  27. 29.

    Item 45 Use member function templates to accept “all compatible

    types ” 利用成員函式模板 接受所有相容型別 Things to Remember Use member function templates to generate functions that accept all compatible types . If you declare member templates for generalized copy construction or generalized assignment , you 'll still need to declare the normal copy constructor and copy assignment operator , too . 29 / 52
  28. 30.

    Item 46 Define non-member function inside templates when type conversions

    are desired 需要型別轉換時請為模板定義非成員函式 30 / 52
  29. 31.

    Item 46 Define non - member function inside templates when

    type conversions are desired 需要型別轉換時 請為模板定義非成 員函式 Item 24 explains why only non -member functions are eligible for implicit type conversions on all arguments . However , once the R a t i o n a l and o p e r a t o r * become templates , the expressions fail to compile . t e m p l a t e < t y p e n a m e T > c l a s s R a t i o n a l { p u b l i c : R a t i o n a l ( c o n s t T & n u m e r a t o r = 0 , c o n s t T & d e n o m i n a t o r = 0 ) ; c o n s t T e n u m e r a t o r ( ) c o n s t ; c o n s t T d e n o m i n a t o r ( ) c o n s t ; … } ; t e m p l a t e < t y p e n a m e T > c o n s t R a t i o n a l < T > o p e r a t o r * ( c o n s t R a t i o n a l < T > & l h s , c o n s t R a t i o n a l < T > & r h s ) { … } R a t i o n a l < i n t > o n e H a l f ( 1 , 2 ) ; R a t i o n a l < i n t > r e s u l t = o n e H a l f * 2 ; / / c o m p i l e e r r o r ! Compilers know they 're supposed to instantiate from some function named o p e r a t o r * taking two parameters of type R a t i o n a l < T >, but in order to do that instantiation , they have to figure our what T is . The problem is , they can 't . 31 / 52
  30. 32.

    Item 46 Define non - member function inside templates when

    type conversions are desired 需要型別轉換時 請為模板定義非成 員函式 Implicit type conversion functions are never considered during template argument deduction . Class templates don 't depend on template argument deduction (that process applies only to function templates ). I think that 's why we need C l a s s N a m e < T > to instantiate class templates , while f u n c N a m e < T > is unnecessary . t e m p l a t e < t y p e n a m e T > c l a s s R a t i o n a l { p u b l i c : R a t i o n a l ( c o n s t T & n u m e r a t o r = 0 , c o n s t T & d e n o m i n a t o r = 0 ) ; c o n s t T e n u m e r a t o r ( ) c o n s t ; c o n s t T d e n o m i n a t o r ( ) c o n s t ; … f r i e n d c o n s t R a t i o n a l o p e r a t o r * ( c o n s t R a t i o n a l & l h s , c o n s t R a t i o n a l & r h s ) ; / / l i n k e r r o r ! } ; t e m p l a t e < t y p e n a m e T > c o n s t R a t i o n a l < T > o p e r a t o r * ( c o n s t R a t i o n a l < T > & l h s , c o n s t R a t i o n a l < T > & r h s ) { … } The definition of o p e r a t o r * for R a t i o n a l < T > is not instantiated as long as R a t i o n a l < T > is instantiated . 32 / 52
  31. 33.

    Item 46 Define non - member function inside templates when

    type conversions are desired 需要型別轉換時 請為模板定義非成 員函式 Simplest approach is to merge the body of o p e r a t o r * into its declaration inside c l a s s R a t i o n a l < T > . t e m p l a t e < t y p e n a m e T > c l a s s R a t i o n a l { p u b l i c : … f r i e n d c o n s t R a t i o n a l o p e r a t o r * ( c o n s t R a t i o n a l & l h s , c o n s t R a t i o n a l & r h s ) { r e t u r n R a t i o n a l ( l h s . n u m e r a t o r ( ) * r h s . n u m b e r a t o r ( ) , l h s . d e n o m i n a t o r ( ) * r h s . d e n o m i n a t o r ( ) ) ; } } ; The use of friendship has nothing to do with a need to access non - public parts of the class . The only way to declare a non -member function inside a class is to make it a f r i e n d . 33 / 52
  32. 34.

    Item 46 Define non - member function inside templates when

    type conversions are desired 需要型別轉換時 請為模板定義非成 員函式 Things to Remember When writing a class template that offers functions related to the template that support implicit type conversions on all parameters , define those functions as friends inside the class template . 34 / 52
  33. 35.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 35 / 52
  34. 36.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 Utility function a d v a n c e of STL moves a specified iterator a specified distance . t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d a d v a n c e ( I t e r T & i t e r , D i s t T d ) ; Conceptually , a d v a n c e just does i t e r + = d , but a d v a n c e can 't actually be implemented that way , because only random access iteartors support the + = operation . Less powerful iterator type have to implement a d v a n c e by iteratively applying + + or - - by d times . 36 / 52
  35. 37.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 There are five categories of iterators 1. Input iterators : move only forward , move only one step at a time , and read from what they 're pointing to . Representives are i s t r e a m _ i t e r a t o r s . 2. Output iterators : move only forward , move only one step at a time , and write to what they 're pointer to . o s t r e a m _ i t e r a t o r s epitomize this category . 3. Forward iterators : move only forward , and can do everything both input and output iterators can do . C ++11's s t d : : f o r w a r d _ l i s t would be an example . 4. Bidirectional iterators : add to forward iterators the ability to move backward . Iterators for STL 's l i s t , s e t , m u l t i s e t , m a p , and m u l t i m a p fall into this category . 5. Random access iterator : add to bidirectional iterators the ability to perform “iterator arithmetic ”. For instance , STL 's v e c t o r , d e q u e , and s t r i n g . 37 / 52
  36. 38.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 Pseudo implementation of a d v a n c e could be as following . t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d a d v a n c e ( I t e r & i t e r , D i s t T d ) { i f ( … / * i t e r i s a r a n d o m a c c e s s i t e r a t o r * / … ) { i t e r + = d ; } e l s e { i f ( d > = 0 ) { w h i l e ( d - - ) + + i t e r ; } e l s e { w h i l e ( d + + ) - - i t e r ; } } } C ++ has a tag struct in the standard library that serves to identify them . s t r u c t i n p u t _ i t e r a t o r _ t a g { } ; s t r u c t o u t p u t _ i t e a r t o r _ t a g { } ; s t r u c t f o r w a r d _ i t e r a t o r _ t a g : p u b l i c i n p u t _ i t e r a t o r _ t a g { } ; s t r u c t b i d i r e c t i o n a l _ i t e r a t o r _ t a g : p u b l i c f o r w a r d _ i t e r a t o r _ t a g { } ; s t r u c t r a n d o m _ a c c e s s _ i t e r a t o r _ t a g : p u b l i c b i d i r e c t i o n a l _ i t e r a t o r _ t a g { } ; Problem : how to tag types with these tags ? Traits . Traits aren 't a keyword or a predefined construct in C ++; they 're a technique and a convention followed by C ++ programmers . 38 / 52
  37. 39.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 Traits must work with built - in types means that things like nesting information inside types won 't do (ref . P 206), because there 's no way to nest information inside pointers . Standard technique is to put the tag into a template and one or more specializations of that template . The way i t e r a t o r _ t r a i t s works is that for each type I t e r T , a t y p e d e f named i t e r a t o r _ c a t e g o r y is declared in the struct i t e r a t o r _ t r a i t s < I t e r T >. This t y p e d e f identifies the iterator category of I t e r T . i t e r a t o r _ t r a i t s implements this in two parts . 1. It imposes the requirement that any user -defined iterator type must contain a nested t y p e d e f named i t e r a t o r _ c a t e g o r y that identifies the appropriate tag struct . 2. i t e r a t o r _ t r a i t s offers a partial template specialization for pointer types . Pointers act as random access iterators . 39 / 52
  38. 40.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 t e m p l a t e < … > c l a s s d e q u e { p u b l i c : c l a s s i t e a r t o r { p u b l i c : t y p e d e f r a n d o m _ a c c e s s _ i t e r a t o r _ t a g i t e r a t o r _ c a t e g o r y ; } } / / p a r r o t s b a c k t h e i t e r a t o r c l a s s ' s n e s t e d t y p e d e f t e m p l a t e < t y p e n a m e I t e r T > s t r u c t i t e r a t o r _ t r a i t s { t y p e d e f t y p e n a m e I t e r T : : i t e r a t o r _ c a t e g o r y i t e r a t o r _ c a t e g o r y ; } / / p a r t i a l s p e c i a l i z a t i o n f o r p o i n t e r t y p e s t e m p l a t e < t y p e n a m e T > s t r u c t i t e r a t o r _ t r a i t s < T * > { t y p e d e f r a n d o m _ a c c e s s _ i t e r a t o r _ t a g i t e r a t o r _ c a t e g o r y ; } Once these are set up , the iterator category of T could be retrieved via t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < T > : : i t e r a t o r _ c a t e g o r y . Problem : how to use the type in a conditional statement ? i f ( t y p e n a m e … = = ? ? ? ) 40 / 52
  39. 41.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d a d v a n c e ( I t e r T & i t e r , D i s t T d ) { i f ( t y p e i d ( t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < I t e r T > : : i t e r a t o r _ c a t e g o r y ) = = t y p e i d ( s t d : : r a n d o m _ a c c e s s _ i t e r a t o r _ t a g ) ) { i t e r + = d ; / / e r r o r w h e n ` I t e r T ` i s ` s t d : : l i s t < i n t > : : i t e r a t o r ` } e l s e { i f ( d > = 0 ) { w h i l e ( d - - ) + + i t e r ; } e l s e { w h i l e ( d + + ) - - i t e r ; } } } Compile -time conditional construct for types (compile -time polymorphism ). t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d d o A d v a n c e ( I t e r T & i t e r , D i s t T d , s t d : : r a n d o m _ a c c e s s _ i t e r a t o r _ t a g ) { i t e r + = d ; } t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d d o A d v a n c e ( I t e r T & i t e r , D i s t T d , s t d : : b i d i r e c t i o n a l _ i t e r a t o r _ t a g ) { i f ( d > = 0 ) { w h i l e ( d - - ) + + i t e r ; } e l s e { w h i l e ( d + + ) - - i t e r ; } } t e m p l a t e < t y p e n a m e I t e r T , t y p e n a m e D i s t T > v o i d a d v a n c e ( I t e r T & i t e r , D i s t T d ) { d o A d v a n c e ( i t e r , d , t y p e n a m e s t d : : i t e r a t o r _ t r a i t s < I t e r T > : : i t e r a t o r _ c a t e g o r y ( ) ) ; } 41 / 52
  40. 42.

    Item 47 Use traits classes for information about types 請使用

    traits classes 表現型別資訊 Things to Remember Traits classes make information about types available during compilation . They 're implemented using templates and template specializations . In conjunction with overloading , traits classes make it possible to perform compile -time i f - e l s e tests on types . 42 / 52
  41. 44.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    Template metaprogramming (TMP ) is the process of writing template - based C ++ program that execute during compilation . When a TMP program finishes running , its output — pieces of C ++ source code instantiated from templates — is then compiled as usual . If this doesn 't strike you as just plain bizarre — you 're not thinking about it hard enough . Yes , TMP was discovered , not invented . Some kinds of errors that are usually detected at runtime can be found during compilation . C ++ programs making use of TMP can be more efficient in just about every way : smaller executables , shorter runtimes , lesser memory requirements . A consequence of shifting work from runtime to compile -time is that compilation takes longer . The traits approach is TMP . Remember , traits enable compile -time i f - e l s e computations on types (compile -time polymorphism ). 44 / 52
  42. 45.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    TMP has no real looping construct , so the effect of loops is accomplished via recursion . t e m p l a t e < u n s i g n e d n > s t r u c t F a c t o r i a l { e n u m { v a l u e = n * F a c t o r i a l < n - 1 > : : v a l u e } ; } ; t e m p l a t e < > s t r u c t F a c t o r i a l < 0 > { e n u m { v a l u e = 1 } ; } ; t e m p l a t e < u n s i g n e d n > u n s i g n e d F a c t o r i a l F u n c ( ) { r e t u r n n * F a c t o r i a l F u n c < n - 1 > ( ) ; } t e m p l a t e < > u n s i g n e d F a c t o r i a l F u n c < 0 > ( ) { r e t u r n 1 ; } i n t m a i n ( ) { s t d : : c o u t < < F a c t o r i a l < 5 > : : v a l u e < < s t d : : e n d l ; / / c o m p i l e - t i m e s t d : : c o u t < < F a c t o r i a l < 1 0 > : : v a l u e < < s t d : : e n d l ; / / c o m p i l e - t i m e s t d : : c o u t < < F a c t o r i a l F u n c < 5 > ( ) < < s t d : : e n d l ; / / r u n t i m e s t d : : c o u t < < F a c t o r i a l F u n c < 1 0 > ( ) < < s t d : : e n d l ; / / r u n t i m e } What TMP can accomplish beyond F a c t o r i a l Ensuring dimensional unit correctness Optimizing matrix operations Generating custom design pattern implementations Debuggers for template metaprogramming ? Ha ! 45 / 52
  43. 46.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    TMP workaround for “Penguin is -a Bird ” problem of Item 32 in C ++11 Not working for dynamic binding (runtime polymorphism ) C ++ does not allow virtual function template c l a s s B i r d { p u b l i c : v i r t u a l v o i d f l y ( ) { s t d : : c o u t < < " b i r d i s f l y i n g " < < s t d : : e n d l ; } } ; c l a s s P e n g u i n : p u b l i c B i r d { p u b l i c : t e m p l a t e < t y p e n a m e T = b o o l > v o i d f l y ( ) { T c o n s t d i s a b l e d = f a l s e ; s t a t i c _ a s s e r t ( d i s a b l e d , " P e n g u i n s d o n o t f l y : - ( " ) ; } / / v i r t u a l v o i d f l y ( ) { / / t h r o w s t d : : l o g i c _ e r r o r ( " P e n g u i n s d o n o t f l y : - ( " ) ; / / } } ; i n t m a i n ( ) { P e n g u i n p e n g u i n ; p e n g u i n . f l y ( ) ; / / c o m p i l e e r r o r ! P e n g u i n p P e n g u i n = n e w P e n g u i n ( ) ; p P e n g u i n - > f l y ( ) ; / / c o m p i l e e r r o r ! B i r d * p B i r d = n e w P e n g u i n ( ) ; p B i r d - > f l y ( ) ; / / o k , " b i r d i s f l y i n g " } 46 / 52
  44. 47.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    Build custom traits with SFINAE (Substitution failure is not an error ) s t r u c t T e s t { t y p e d e f i n t f o o ; } t e m p l a t e < t y p e n a m e T > v o i d f ( t y p e n a m e T : : f o o ) { } / / D e f i n i t i o n # 1 t e m p l a t e < t y p e n a m e T > v o i d f ( T ) { } / / D e f i n i t i o n # 2 i n t m a i n ( ) { f < T e s t > ( 4 2 ) ; / / c a l l s # 1 f < i n t > ( 4 2 ) ; / / c a l l s # 2 w i t h o u t e r r o r ( e v e n t h o u g h t h e r e ' s n o ` i n t : : f o o ` ) } / / ` t y p e n a m e T : : * ` c h e c k s w h e t h e r ` T ` h a s m e m b e r s t e m p l a t e < t y p e n a m e T , t y p e n a m e = t y p e n a m e T : : * > s t r u c t i s _ i t e r a t a b l e _ c o n t a i n e r { s t a t i c b o o l c o n s t v a l u e = s t d : : i s _ c o n v e r t i b l e < t y p e n a m e i t e r a t o r _ t r a i t s < t y p e n a m e T : : i t e r a t o r > : : i t e r a t o r _ c a t e g o r y , s t d : : i n p u t _ i t e r a t o r _ t a g > : : v a l u e ; } ; / / ` e n a b l e _ i f < > ` c h e c k s w h e t h e r ` T ` i s i t e r a t a b l e t e m p l a t e < t y p e n a m e T , t y p e n a m e = t y p e n a m e s t d : : e n a b l e _ i f < i s _ i t e r a t a b l e _ c o n t a i n e r < T > : : v a l u e > : : t y p e > > s t r u c t i s _ k e y _ v a l u e _ c o n t a i n e r { t y p e d e f c h a r Y e s [ 1 ] ; t y p e d e f c h a r N o [ 2 ] ; t e m p l a t e < t y p e n a m e U > s t a t i c Y e s & t e s t ( t y p e n a m e U : : k e y _ t y p e * ) ; t e m p l a t e < t y p e n a m e U > s t a t i c N o & t e s t ( . . . ) ; s t a t i c b o o l c o n s t v a l u e = s i z e o f ( t e s t < T > ( n u l l p t r ) ) = = s i z e o f ( Y e s ) ; } 47 / 52
  45. 48.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    Further Readings GitHub – dropbox / json 11 (JSON parsing & serializing in C ++11) c l a s s J s o n f i n a l { … / / I m p l i c i t c o n s t r u c t o r : a n y t h i n g w i t h a t o _ j s o n ( ) f u n c t i o n . t e m p l a t e < c l a s s T , c l a s s = d e c l t y p e ( & T : : t o _ j s o n ) > J s o n ( c o n s t T & t ) : J s o n ( t . t o _ j s o n ( ) ) { } / / I m p l i c i t c o n s t r u c t o r : m a p - l i k e o b j e c t s ( s t d : : m a p , s t d : : u n o r d e r e d _ m a p , … ) t e m p l a t e < c l a s s M , t y p e n a m e s t d : : e n a b l e _ i f < s t d : : i s _ c o n s t r u c t i b l e < s t d : : s t r i n g , t y p e n a m e M : : k e y _ t y p e > : : v a l u e & & s t d : : i s _ c o n s t r u c t i b l e < J s o n , t y p e n a m e M : : m a p p e d _ t y p e > : : v a l u e , i n t > : : t y p e = 0 > J s o n ( c o n s t M & m ) : J s o n ( s t d : : m a p < s t d : : s t r i n g , J s o n > ( m . b e g i n ( ) , m . e n d ( ) ) ) { } / / I m p l i c i t c o n s t r u c t o r : v e c t o r - l i k e o b j e c t s ( s t d : : l i s t , s t d : : v e c t o r , s t d : : s e t , … ) t e m p l a t e < c l a s s V , t y p e n a m e s t d : : e n a b l e _ i f < s t d : : i s _ c o n s t r u c t i b l e < J s o n , t y p e n a m e V : : v a l u e _ t y p e > : : v a l u e , i n t > : : t y p e = 0 > J s o n ( c o n s t V & v ) : J s o n ( s t d : : v e c t o r < J s o n > ( v . b e g i n ( ) , v . e n d ( ) ) ) { } … } Stack Overflow – Template instantiation details of GCC and MS compilers Super Template Tetris (game as a C ++ template metaprogram ) 48 / 52
  46. 49.

    Item 48 Be aware of template metaprogramming 認識 template 超編程

    Things to Remember Template metaprogramming can shift work from runtime to compile - time , thus enabling earlier error detection and higher runtime performance . TMP can be used to generate custom code based on combinations of policy choices , and it can also be used to avoid generating code inappropriate for particular types . 49 / 52
  47. 51.

    Appendix A Overload Resolution “The C ++ Programming Language 4/e

    ” §12.3.1 The idea is to invoke the function that is the best match to the arguments and give a compile -time error if no function is the best match 1. Exact match ; that is , match using no or only trivial conversion (e .g ., array name to pointer , function name to pointer to function , and T to c o n s t T ) 2. Match using promotions ; that is , integral promotions (b o o l to i n t , c h a r to i n t , s h o r t to i n t , and their u n s i g n e d counterparts ) and f l o a t to d o u b l e 3. Match using standard conversion (e .g ., i n t to d o u b l e , d o u b l e to i n t , d o u b l e to l o n g d o u b l e, D e r i v e d * to B a s e * , T * to v o i d * , i n t to u n s i g n e d i n t ) 4. Match using user -defined conversions (e .g ., d o u b l e to c o m p l e x < d o u b l e > ) 5. Match using the ellipsis . . . in a function declaration If two matches are found at the highest level where a match is found , the call is rejected as ambiguous . 51 / 52
  48. 52.

    Appendix B Function Template Overload Resolution “The C ++ Programming

    Language 4/e ” §23.5.3, §25.3.2 For each template we find the specialization that is best for the set of function arguments 1. Find the set of function template specializations — do this by considering each function template and deciding which template arguments , if any , would be used if no other function templates or functions of the same name were in scope 2. If two function templates can be called and one is more specialized than the other , consider only the most specialized template function in the following steps 3. Do overload resolution for this set of functions , plus any ordinary functions , as for ordinary functions . If a function template 's arguments has been determined by template argument deduction , that argument cannot also have promotions , standard conversion , or user -defined conversions applied 4. If a function and a specialization are equally good matches , the ordinary function is preferred The primary template is the one used to determine if a use is valid and takes part in overload resolution . Only after a primary template has been chosen are specializations considered 52 / 52