Chapter I STL Overview

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;
}

As far as I'm concerned, when I'm dealing with i1_examples of i1_ members, I2_similar members change.
Because they use the same template class.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;
}

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;
}

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;
}

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;
}

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;
}

The results are as follows:

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;
}

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;
}

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:

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;
}

Output result:

hash<long> -> hash<T> ∵hash<long>并没有被显式声明,故调用默认模板

hash<char> -> hash<char> ∵hash<char>已经被显式声明,故调用特定模板

hash<unsigned char> -> hash<unsigned char> 原因同上

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;
}

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;
}

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;
}

Output result:

Analysis:

  • I'm sorry, prefix increation.: Upfront ++, similar++iIn this case, priority is given to i-activated and reused.

    // prefix: increment and then fetch
    INT& operator++(){
        ++(this->m_i);
        return *this;
    }
  • Postfix inclusion: Assemblage ++, similar toi++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;        // 返回没有被操作的目标对象
    }
  • Dereference: e.g. by decitation, returns the corresponding type of address of the target object.

    // deference
    int& operator*() const{
    std::cout << "dereference" << '\n';
    return (int&)m_i;
    }

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;
}

I'll be right behind you. [first, last]Let's start with first.
Not reaching last.

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;
}

Type of template () Reloaded to make it a functor

Relaid Post

STL Source Analysis - Chapter II

第二章 空间配置器(allocator) 1.空间配置器...

0 0 Number of ballots cast
Article Rating
Subscription comments
Organisation
I don't know.
0 Comments
Oldest
Latest Most votes
Inline feedback
View all comments
Remember to rest.
0
Please comment on your thoughts.x
()
x