STL Source Analysis

Chapter I STL Overview

Estimated reading time 10 minutes 118 views

The C++ syntax that might confuse you.

1. Status membership in category 1

When initializing multiple examples of the template category, these examples are shared with the status members of the template category (the same type).

#include <iostream>

template<typename T>
class myClass
{
public:
	static int i1_;
	static float f1_;
};

// int type template
int myClass<int>::i1_ = 1;
float myClass<int>::f1_ = 1.1f;

// float type template
int myClass<float>::i1_ = 2;
float myClass<float>::f1_ = 2.2f;

int main()
{
	myClass<int> ic1_, ic2_;
	myClass<float> fc1_, fc2_;

	std::cout
	    << ic1_.i1_ << ' '
		<< ic1_.f1_ << ' '
		<< ic2_.i1_ << ' '
		<< ic2_.f1_ << ' '
		<< fc1_.i1_ << ' '
		<< fc1_.f1_ << ' '
		<< fc2_.i1_ << ' '
		<< fc2_.f1_ << ' ';
	return 0;
}
C++

As a matter of fact, when i1_examples i1_members are operated, the i2_systems also change because they use the same template category.myClass<int> Likewise, fc1_ and fc2_.

2. Special design for template categories

    When initializing an example, the compiler matches the type of pointer provided.

    #include <iostream>
    
    // generalized
    template<class T, class O>
    struct tc
    {
    	tc() { std::cout << "I, O" << '\n'; }
    };
    
    // specialized
    template<class T>
    struct tc<T*, T*>
    {
    	tc() { std::cout << "T*, T*" << '\n'; }
    };
    
    // specialized
    template<class T>
    struct tc<const T*, T*>
    {
    	tc() { std::cout << "const T*, T*" << '\n'; }
    };
    
    int main()
    {
    	tc<int, char> obj1;
    	tc<int*, int*> obj2;
    	tc<const int*, int*> obj3;
    	tc<const int*, const int*> obj4;
    	tc<int*, const int*> obj5;
    	return 0;
    }
    C++

    Output result:

    Image_10.png

    If not matched, the default template class is automatically selected.

    3. The members of the template category can re-establish the template

      #include <iostream>
      
      class alloc{};
      
      template<class T, class Alloc = alloc>
      class vector{                       // template class
      public:
          typedef T value_type;           // T -> value_type
          typedef value_type* iterator;   // value_type* -> iterator
      
          template<class I>               // template member function
          void insert(iterator position, I first, I last){ std:: cout << "insert()" << '\n'; }
      };
      
      int main(){
          int ia[5] = {0, 1, 2, 3, 4};
      
          vector<int> x;
          vector<int>::iterator ite = nullptr;
          x.insert(ite, ia, ia + 5);
      
          return 0;
      }
      C++

      4. Template arguments can set default values based on the previous template parameter

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        // 根据前一个参数值T, 设定下一个参数Sequence的默认值为deque<T>
        template<class T, class Sequence = deque<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c;
        };
        
        int main() {
            stack<int> x;
            return 0;
        }
        C++

        Output result:

        Image_11.png

        From the output results, it can be seen that, in initializing the Stack instance, Sequence in the Stack will first initiate a deque instance based on the parameters in the template and then exemplify the Stack.

        It is generally known that when a template class is initialized, the template in the template category gives priority to the initial examples of the other template class if there are other template classes.

        Example 1, as follows:

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        // 先初始化deque的实例
        template<class T, class Sequence = deque<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c;
        };
        
        // 先初始化stack的实例
        template<class T, class Test = stack<T>>
        class test
        {
        public:
            test() { std::cout << "test()" << '\n'; }
        private:
            Test t;
        };
        
        int main() {
            test<int> t_;
            return 0;
        }
        C++

        Output result:

        Image_12.png

        Example 2 as follows:

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        template<class T, class Test = deque<T>>
        class test
        {
        public:
            test() { std::cout << "test()" << '\n'; }
        private:
            Test t;
        };
        
        template<class T, class Sequence = deque<T>, class test_ = test<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c; test_ _t;
        };
        
        int main() {
            stack<int> t_;
            return 0;
        }
        C++

        The results are as follows:

        Image_13.png

        Process extrapolation:

        • Example deque - > deque()
        • Example
        • Example
        • CaseStack Finish - >back()

        5. Template parameters for template class with Non-type

          #include <iostream>
          #include <cstddef>
          
          class alloc {};
          
          inline size_t __deque_buf_size(size_t n, size_t sz) {
              return n != 0 ? n : (sz < 512) ? size_t(512 / sz) : size_t(1);
          }
          
          template<class T, class Ref, class Ptr, size_t BufSiz>
          struct __deque_iterator {
              typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
              typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
              static size_t buffer_size() { return __deque_buf_size(BufSiz, sizeof(T)); }
          };
          
          template<class T, class Alloc = alloc, size_t BufSiz = 0>
          class deque {
          public: typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
          };
          
          int main() {
              std::cout << deque<int>::iterator::buffer_size() << '\n';
              std::cout << deque<int, alloc, 64>::iterator::buffer_size() << '\n';
              return 0;
          }
          C++

          In CategorydequeMembersiteratorUse parameters in templates for initializationBufSizIt's one.valueNot one.type

          It follows that the template accepts non-typesnon-type.

          6. Bound Friend Template

            A specific example of a class template corresponds to a specific example of its alumni function template.

            #include <iostream>
            #include <cstddef>
            
            class alloc {};
            
            template<class T, class Alloc = alloc, size_t  BufSiz = 0>
            class deque{
            public: deque() { std::cout << "deque()" << '\n'; }
            };
            
            template<class T, class Sequence>
            class stack;
            
            template<class T, class Sequence>
            bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
            
            template<class T, class Sequence>
            bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
            
            template<class T, class Sequence = deque<T>>
            class stack{
                // style 1
                friend bool operator== <T> (const stack<T>& , const stack<T>& );
                friend bool operator< <T> (const stack<T>& , const stack<T>& );
                // style 2
                friend bool operator== <T> (const stack&, const stack&);
                friend bool operator< <T> (const stack&, const stack&);
                // style 3
                friend bool operator== <> (const stack&, const stack&);
                friend bool operator< <> (const stack&, const stack&);
                // invalid style
                //friend bool operator== (const stack&, const stack&);
                //friend bool operator< (const stack&, const stack&);
            public: stack() { std::cout << "stack()" << '\n'; }
            private: Sequence c;
            };
            
            template<class T, class Sequence>
            bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y){
                std::cout << "operator==()" << '\t';
                return true;
            }
            
            template<class T, class Sequence>
            bool operator< (const stack<T, Sequence>& x, const stack<T, Sequence>& y){
                std::cout << "operator<()" << '\t';
                return true;
            }
            
            int main(){
                stack<int> x;
                stack<int> y;
            
                std::cout << (x == y) << '\n';
                std::cout << (x < y) << '\n';
            
                //stack<int> y_;
                // invalid
                // std::cout << (x == y_) < '\n';   // no match for this
                // std::cout << (x < y_) << '\n';   // no match for this
            
                return 0;
            }
            C++

            General steps: Use the aluminum function within the template class to declare the operator to be reloaded, and (must) perform the aluminum function for the declaration.

            Output result:

            Image_14.png

            7-Standard Manifest Statement

              #include <iostream>
              
              #define __STL_TEMPLATE_NULL template<>
              
              template<class Key>
              struct hash{
                  void operator() () { std::cout << "hash<T>" << '\n'; }
              };
              
              // explicit specialization
              __STL_TEMPLATE_NULL struct hash<char>{
                  void operator() () { std::cout << "hash<char>" << '\n'; }
              };
              
              __STL_TEMPLATE_NULL struct hash<unsigned char>{
                  void operator() () { std::cout << "hash<unsigned char>" << '\n'; }
              };
              
              int main(){
                  hash<long> t1;
                  hash<char> t2;
                  hash<unsigned char> t3;
              
                  t1();
                  t2();
                  t3();
              
                  return 0;
              }
              C++

              Output result:

              Image_15.png

              hash<long> -> hash<T> ∵hash<long>并没有被显式声明,故调用默认模板
              
              hash<char> -> hash<char> ∵hash<char>已经被显式声明,故调用特定模板
              
              hash<unsigned char> -> hash<unsigned char> 原因同上
              C++

              In this way, the behaviour of different types of templates can be defined.

              8. Generation and use of temporary objects

                #include <vector>
                #include <iostream>
                #include <algorithm>
                
                template<typename T>
                class print{
                public: void operator() (const T& elem) { std::cout << elem << ' '; }
                };
                
                int main(){
                    int ia[6] = {0, 1, 2, 3, 4, 5, 6};
                    std::vector<int> iv(ia, ia + 5);
                    // print<int> is an unnamed object but not a invoking operation
                    std::for_each(iv.begin(), iv.end(), print<int>());
                
                    return 0;
                }
                C++

                Temporary objects are anonymous (unnamed objects). Method of achievement

                Reload the symbol: void operator() (const T& elem) { std::cout << elem << ' '; }

                Do not need to state the name of the object, call directly by ()std::for_each(iv.begin(), iv.end(), print<int>());

                Life cycle of the temporary object

                StatusActions
                Startfor_each()(或其他函数)第一次调用print<int>()(临时对象)
                Endfor_each()结束时

                9. Direct initialization of static constant members within class

                  #include <iostream>
                  
                  template<typename T>
                  class testClass{
                      public: expedient
                      static const int _datai = 5;
                      static const long _datal = 3l;
                      static const char _datac = 'c';
                  };
                  
                  int main(){
                      std::cout << testClass<int>::_datai << '\n';
                      std::cout << testClass<long>::_datal << '\n';
                      std::cout << testClass<char>::_datac << '\n;
                  
                      return 0;
                  }
                  C++

                  10. Incentive/description/deference operator

                    This operator can be used for the movement or take-off of the pointer (deference) of an iterative device.

                    Move operations can be divided into prefix and postfix.

                    #include <iostream>
                    
                    class INT{
                        friend std::ostream& operator<<(std::ostream& os, const INT& i);
                    public: INT(int i): m_i(i) {};
                    
                        // prefix: increment and then fetch
                        INT& operator++(){
                            ++(this->m_i);
                            return *this;
                        }
                    
                        // postfix: fetch and then increment
                        const INT operator++(int){
                            INT temp = *this;
                            ++(*this);
                            return temp;
                        }
                    
                        // prefix: minus and then fetch
                        INT& operator--(){
                            --(this->m_i);
                            return *this;
                        }
                    
                        // postfix: fetch and then increment
                        const INT operator--(int){
                            INT temp = *this;
                            --(*this);
                            return temp;
                        }
                    
                        // deference
                        int& operator*() const{
                            std::cout << "dereference" << '\n';
                            return (int&)m_i;
                        }
                    
                    private: int m_i;
                    };
                    
                    std::ostream& operator<<(std::ostream& os, const INT& i){
                        os << '[' << i.m_i << ']';
                        return os;
                    }
                    
                    int main(){
                        INT I(5);
                        std::cout << I++ << '\n';
                        std::cout << ++I << '\n';
                        std::cout << I-- << '\n';
                        std::cout << --I << '\n';
                        std::cout << *I << '\n';
                        return 0;
                    }
                    C++

                    Output result:

                    Image_16.png

                    Analysis:

                    • Prefix increment: is prefix ++, similar++iIn this case, priority is given to i-activated and reused.
                    // prefix: increment and then fetch
                       INT& operator++(){
                           ++(this->m_i);
                           return *this;
                       }
                    C++
                    • Postfix input: i.e. rear ++, similari++In this case, priority is given to i and then to operate it.
                    // postfix: fetch and then increment
                      const INT operator++(int){
                          INT temp = *this;   // 声明一个临时对象temp用于存储要操作的目标对象,即还没被操作的目标对象
                          ++(*this);
                          return temp;        // 返回没有被操作的目标对象
                      }
                    C++
                    • Dereference: e.g. unquote, returns the corresponding type of address of the target object.
                    // deference
                    int& operator*() const{
                        std::cout << "dereference" << '\n';
                        return (int&)m_i;
                    }
                    C++

                    11. Inter-dependency expression [ ]

                      template<class Iterator, class T>
                      InputIterator find(Iterator first, Iterator Last, const T& value){
                          while(first != last & *first != value) ++first;
                          return first;
                      }
                      
                      template<class InputIterator, class Function>
                      Function for_each(InputIterator first, InputIterator last, Function f){
                          for(; first != last; ++first) f(*first)
                          return f;
                      }
                      C++

                      [first, last] [first, last] so that the pointer starts first and does not reach last (first position of last = > last - 1).

                      Image_17.png

                      12. Function call operator (opator())

                      #include <iostream>
                      
                      template<class T>
                      struct plus{
                          T operator() (const T& x, const T& y) const { return x + y; }
                      };
                      
                      template<class T>
                      struct minus{
                          T operator() (const T& x, const T& y) const { return x - y; }
                      };
                      
                      int main(){
                          plus<int> plusobj;      // functor object of plus
                          minus<int> minusobj;    // functor object of minus
                      
                          // invoke the functor by the corresponding instances
                          std::cout << plusobj(5, 10) << '\n';
                          std::cout << minusobj(10, 5) << '\n';
                      
                          // create the temporary object as the functor
                          std::cout << plus<int>()(30, 20) << '\n';
                          std::cout << minus<int>()(210, 10) << '\n';
                      
                          return 0;
                      }
                      C++

                      The template classes () are reloaded here as a functor function

                      Leave a Comment

                      Share this Doc

                      Chapter I STL Overview

                      Or copy link

                      CONTENTS
                      Remember to rest.