cppPrimer学习16th

cppPrimer学习16th

TODO

16.22
16.23

16.1

给出实例化的定义
我们在调用模块的时候,传递确定的参数,编译器为我们构造真实的函数或者对象

16.2

// 16.2 编写你自己的compare版本

#include <iostream>

template <typename T>
int compare(const T &a, const T &b)
{
    if (a > b)
        return 1;
    if (a < b)
        return -1;
    return 0;
}

int main(int argc, char const *argv[])
{
    std::cout << compare(1, 2) << std::endl;
    while (1)
        ;
    return 0;
}

16.3

提示未定义 operator<
error: no match for 'operator>' (operand types are 'const Sales_data' and 'const Sales_data')

// 16.2 编写你自己的compare版本

#include <iostream>

class Sales_data
{
private:
    int a;

public:
    Sales_data() : a(0) {}
    ~Sales_data() {}
};

template <typename T>
int compare(const T &a, const T &b)
{
    if (a > b)
        return 1;
    if (a < b)
        return -1;
    return 0;
}

int main(int argc, char const *argv[])
{
    Sales_data a;
    Sales_data b;
    std::cout << compare(1, 2) << std::endl;
    std::cout << compare(a, b) << std::endl;
    while (1)
        ;
    return 0;
}

16.4

// 16.4 编写行为类似标准库find算法的模版.函数需要两个模版类型参数,一个表示函数的迭代器参数,另一个表示值的类型.
//      使你的函数在一个vector<int> 和 list<string>中查找给定值

#include <vector>
#include <list>
#include <string>
#include <iostream>

template <typename it_type, typename value_type>
it_type find(const it_type begin, const it_type end, const value_type val)
{
    for (auto it = begin; it != end; it++)
    {
        if (*it == val)
            return it;
    }
    return end;
}

template <typename it_type, typename value_type>
std::ostream &PrintFind(const it_type &begin, const it_type &end, const value_type &val, std::ostream &o = std::cout)
{
    it_type find_it = find(begin, end, val);
    if (find_it == end)
        return o << "Null to find";
    else
        return o << *find_it;
}

int main(int argc, char const *argv[])
{
    std::vector<int> vs({1, 2, 3, 4, 5, 6, 7, 8, 9});

    std::cout << *find(vs.begin(), vs.end(), 2) << std::endl;
    PrintFind(vs.begin(), vs.end(), 2) << std::endl;
    PrintFind(vs.begin(), vs.end(), 10) << std::endl;

    std::list<std::string> ls({"123", "456", "ABC"});
    PrintFind(ls.begin(), ls.end(), "999") << std::endl;

    while (1)
        ;
    return 0;
}

16.5

// 16.5 编写一个printf 支持任意大小任意元素类型的数组

#include <iostream>
#include <string>
template <typename elem_type, int size>
void print(elem_type (&arr)[size])
{
    for (auto ch : arr)
    {
        std::cout << ch << std::endl;
    }
}
int main(int argc, char const *argv[])
{
    std::string as[] = {"123", "456", "789"};
    int ai[] = {1, 2, 3, 4, 5, 6};
    print(as);
    print(ai);
    while (1)
        ;
    return 0;
}

16.6

//16.6 	你认为一个接受数组实参的标准库函数 begin 和end是如何工作的,定义你自己版本的begin和end
// 		也就是定义 begin(array)和end(array)
#include <iostream>
#include <string>
template <typename elem_type, size_t size>
elem_type *Begin(elem_type (&array)[size])
{
    return array;
}

template <typename elem_type, size_t size>
elem_type *End(elem_type (&array)[size])
{
    return array + size;
}

int main(int argc, char const *argv[])
{
    std::string as[] = {"123", "456", "789"};

    for (auto i = Begin(as); i != End(as); i++)
        std::cout << *i << std::endl;

    while (1)
        ;
    return 0;
}

16.7

//16.7	编写一个 constexpr 模版,返回给定参数的大小
template <typename T, unsigned N>
constexpr unsigned SizeOfArray(const T (&arr)[N])
{
    return N;
}

16.8

//16.8 在97页非关键概念中,c++程序在for喜欢用!= 替换 <  的原因是什么
迭代器一般定义了 != 和= 但是不一定定义了<

16.9

// 16.9 什么是函数模版? 什么是类模版?
函数有参数是模版,编译器自动推断模版参数
类模版,有参数是模版,需要手动定义模版参数的

16.10

//16.10 当一个类模版被实例化,会发生什么?
1. 生成类的非模版函数
2. 生成需要使用的类的模版函数

16.11

// 16.11 修正以下程序
template <typename elemType> class ListItem;
template <typename elemType> class List {
public:
  List<elemType>();
  List<elemType>(const List<elemType> &);
  List<elemType>& operator=(const List<elemType> &);
  ~List();
  void insert(ListItem *ptr, elemType value);
private:
  ListItem *front, *end;
};

// 修改注释部分
template <typename elemType> class ListItem;
template <typename elemType> class List {
public:
  List<elemType>();
  List<elemType>(const List<elemType> &);
  List<elemType>& operator=(const List<elemType> &);
  ~List();
  //void insert(ListItem *ptr, elemType value);
  void insert(ListItem<elemType> *ptr, elemType value);
private:
  //ListItem *front, *end;
  ListItem<elemType> *front, *end;
};

16.12

// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员

#include <vector>
#include <memory>
#include <string>
#include <iostream>
#include <initializer_list>
#include <exception>
#include <algorithm>

template <typename T>
class BlobPtr;
template <typename T>
class Blob;
template <typename T>
class ConstBlobPtr;

template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b);

template <typename T>
class Blob
{
    friend BlobPtr<T>;
    friend ConstBlobPtr<T>;

    friend bool operator==<T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator!=<T>(const Blob<T> &a, const Blob<T> &b);
    // clang-format off
    friend bool operator< <T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator> <T>(const Blob<T> &a, const Blob<T> &b);
    // clang-format on
    friend bool operator<=<T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator>=<T>(const Blob<T> &a, const Blob<T> &b);

    // 以下两个应该是为了外部方便获取类型
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;

private:
    std::shared_ptr<std::vector<T>> data;
    void check(size_type i, const std::string &msg) const;

public:
    Blob() : data(std::make_shared<std::vector<T>>()) {}

    Blob(const Blob<T> &s) : data(std::make_shared<std::vector<T>>(*s.data)) {}
    Blob(Blob<T> &&a) noexcept : data(std::move(a.data)) {}

    Blob &operator=(const Blob<T> &s);
    Blob &operator=(Blob<T> &&) noexcept;

    Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)) {}
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    void push_back(T &val) { data->push_back(val); };
    void push_back(const T &val) { data->push_back(std::move(val)); }
    void pop_back();

    T &back();
    const T &back() const;
    T &front();
    const T &front() const;

    T &operator[](size_type i);
    const T &operator[](size_type i) const;

    BlobPtr<T> begin();
    BlobPtr<T> end();

    ConstBlobPtr<T> cbegin() const;
    ConstBlobPtr<T> cend() const;

    void print(std::ostream &o);
};

template <typename T>
Blob<T> &Blob<T>::operator=(const Blob<T> &s)
{
    data = std::make_shared<std::vector<T>>(*s.data);
    return *this;
}

template <typename T>
Blob<T> &Blob<T>::operator=(Blob<T> &&s) noexcept
{
    if (this != s)
    {
        data = std::move(s.data);
        s.data = nullptr;
    }
    return *this;
}

template <typename T>
void Blob<T>::print(std::ostream &o)
{
    for (auto ch : (*data))
        o << ch << ",";
    o << std::endl;
}

template <typename T>
void Blob<T>::check(size_type i, const std::string &msg) const
{
    if (i >= size())
    {
        throw std::out_of_range(msg);
    }
}

template <typename T>
void Blob<T>::pop_back()
{
    check(0, "empty to pop_back");
    data->pop_back();
}

template <typename T>
T &Blob<T>::back()
{
    check(0, "Get empty Back()");
    return data->back();
}
template <typename T>
const T &Blob<T>::back() const
{
    check(0, "Get empty Back()");
    return data->back();
}

template <typename T>
T &Blob<T>::front()
{
    check(0, "Get empty front()");
    return data->front();
}
template <typename T>
const T &Blob<T>::front() const
{
    check(0, "Get empty front()");
    return data->front();
}

template <typename T>
T &Blob<T>::operator[](size_type i)
{
    check(i, "Get [" + std::to_string(i) + "]");
    return data->at(i);
}

template <typename T>
const T &Blob<T>::operator[](size_type i) const
{
    check(i, "Get [" + std::to_string(i) + "]");
    return data->at(i);
}

template <typename T>
BlobPtr<T> Blob<T>::begin()
{
    return BlobPtr<T>(*this);
}
template <typename T>
BlobPtr<T> Blob<T>::end()
{
    return BlobPtr<T>(*this, size());
}

template <typename T>
ConstBlobPtr<T> Blob<T>::cbegin() const
{
    return ConstBlobPtr<T>(*this);
}
template <typename T>
ConstBlobPtr<T> Blob<T>::cend() const
{
    return ConstBlobPtr<T>(*this, size());
}

//****************************************************************************
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b)
{
    return (*a.data == *b.data);
}
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b)
{
    return !(a == b);
}

template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b)
{
    return std::lexicographical_compare(a.data->begin(), a.data->end(), b.data->begin(), b.data->end());
}

template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b)
{
    return (b < a);
}
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b)
{
    return !(b > a);
}
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b)
{
    return !(a < b);
}

//****************************************************************************
// 定义 Blob::iterator

template <typename T>
bool operator==(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator!=(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator<(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator>(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator<=(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
bool operator>=(const BlobPtr<T> &, const BlobPtr<T> &);

template <typename T>
class BlobPtr
{
private:
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr;
    std::shared_ptr<std::vector<T>> check(size_t i, const std::string &msg) const;

public:
    friend bool operator==<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    friend bool operator!=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    // clang-format off
    friend bool operator< <T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    friend bool operator> <T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    friend bool operator<=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    friend bool operator>=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
    // clang-format on

    BlobPtr() : curr(0) {}
    BlobPtr(const Blob<T> &pt, size_t at = 0) : wptr(pt.data), curr(at) {}
    T &operator*()
    {
        return check(curr, "Get elem[] out of range")->at(curr);
    }
    T &operator[](size_t at)
    {
        return check(at, "Get elem[] out of range")->at(at);
    }
    const T &operator*() const
    {
        return check(curr, "Get elem[] out of range")->at(curr);
    }
    const T &operator[](size_t at) const
    {
        return check(at, "Get elem[] out of range")->at(at);
    }
    const T *operator->() const
    {
        return &this->operator*();
    }
    T *operator->()
    {
        return &this->operator*();
    }
    BlobPtr operator++();
    BlobPtr operator++(int);
    BlobPtr operator--();
    BlobPtr operator--(int);
    BlobPtr &operator+=(size_t);
    BlobPtr &operator-=(size_t);
    BlobPtr operator+(size_t) const;
    BlobPtr operator-(size_t) const;
};

template <typename T>
std::shared_ptr<std::vector<T>> BlobPtr<T>::check(size_t i, const std::string &msg) const
{
    auto spt = wptr.lock();
    if (spt)
    { // 使用之前必须复制到 shared_ptr
        if (i >= spt->size())
            throw std::out_of_range(msg);
    }
    else
    {
        throw std::runtime_error("unbound Blob<T>Ptr");
    }
    return spt;
}

// 前置++,先++,再返回
template <typename T>
BlobPtr<T> BlobPtr<T>::operator++()
{
    check(curr, "++");
    curr++;
    return *this;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{
    auto ret = *this;
    ++*this;
    return *this;
}
// 前置++,先++,再返回
template <typename T>
BlobPtr<T> BlobPtr<T>::operator--()
{
    check(curr, "--");
    curr--;
    return *this;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator--(int)
{
    auto ret = *this;
    --*this;
    return *this;
}

template <typename T>
BlobPtr<T> &BlobPtr<T>::operator+=(size_t off)
{
    curr += off;
    check(curr, "increment past end of Blob<T>Ptr");
    return *this;
}
template <typename T>
BlobPtr<T> &BlobPtr<T>::operator-=(size_t off)
{
    curr -= off;
    check(curr, "increment past end of Blob<T>Ptr");
    return *this;
}

template <typename T>
BlobPtr<T> BlobPtr<T>::operator+(size_t off) const
{
    BlobPtr<T> ret = *this;
    ret += off;
    return ret;
}

template <typename T>
BlobPtr<T> BlobPtr<T>::operator-(size_t off) const
{
    BlobPtr<T> ret = *this;
    ret -= off;
    return ret;
}

//***********            friend           *******************************//
template <typename T>
bool operator==(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return (a.curr == b.curr);
}
template <typename T>
bool operator!=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return !(a == b);
}

template <typename T>
bool operator<(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return (a.curr < b.curr);
}
template <typename T>
bool operator>(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return (b < a);
}
template <typename T>
bool operator<=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return !(a > b);
}
template <typename T>
bool operator>=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
    return !(a < b);
}

// template <typename T>
// std::ostream &operator<<(std::ostream &o, const T &Blob_val)
// {

// }

//****************************************************************************
// 定义 Blob::iterator

template <typename T>
bool operator==(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator!=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator<(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator>(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator<=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator>=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);

template <typename T>
class ConstBlobPtr
{
private:
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr;
    std::shared_ptr<std::vector<T>> check(size_t i, const std::string &msg) const;

public:
    friend bool operator==<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    friend bool operator!=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    // clang-format off
    friend bool operator< <T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    friend bool operator> <T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    friend bool operator<=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    friend bool operator>=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
    // clang-format on

    ConstBlobPtr() : curr(0) {}
    ConstBlobPtr(const Blob<T> &pt, size_t at = 0) : wptr(pt.data), curr(at) {}
    const T &operator*() const
    {
        return check(curr, "Get elem[] out of range")->at(curr);
    }
    const T &operator[](size_t at) const
    {
        return check(at, "Get elem[] out of range")->at(at);
    }
    const T *operator->() const
    {
        return &this->operator*();
    }
    ConstBlobPtr operator++();
    ConstBlobPtr operator++(int);
    ConstBlobPtr operator--();
    ConstBlobPtr operator--(int);
    ConstBlobPtr &operator+=(size_t);
    ConstBlobPtr &operator-=(size_t);
    ConstBlobPtr operator+(size_t) const;
    ConstBlobPtr operator-(size_t) const;
};

template <typename T>
std::shared_ptr<std::vector<T>> ConstBlobPtr<T>::check(size_t i, const std::string &msg) const
{
    auto spt = wptr.lock();
    if (spt)
    { // 使用之前必须复制到 shared_ptr
        if (i >= spt->size())
            throw std::out_of_range(msg);
    }
    else
    {
        throw std::runtime_error("unbound Blob<T>Ptr");
    }
    return spt;
}

// 前置++,先++,再返回
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator++()
{
    check(curr, "++");
    curr++;
    return *this;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator++(int)
{
    auto ret = *this;
    ++*this;
    return *this;
}
// 前置++,先++,再返回
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator--()
{
    check(curr, "--");
    curr--;
    return *this;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator--(int)
{
    auto ret = *this;
    --*this;
    return *this;
}

template <typename T>
ConstBlobPtr<T> &ConstBlobPtr<T>::operator+=(size_t off)
{
    curr += off;
    check(curr, "increment past end of Blob<T>Ptr");
    return *this;
}
template <typename T>
ConstBlobPtr<T> &ConstBlobPtr<T>::operator-=(size_t off)
{
    curr -= off;
    check(curr, "increment past end of Blob<T>Ptr");
    return *this;
}

template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator+(size_t off) const
{
    ConstBlobPtr<T> ret = *this;
    ret += off;
    return ret;
}

template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator-(size_t off) const
{
    ConstBlobPtr<T> ret = *this;
    ret -= off;
    return ret;
}

//***********            friend           *******************************//
template <typename T>
bool operator==(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return (a.curr == b.curr);
}
template <typename T>
bool operator!=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return !(a == b);
}

template <typename T>
bool operator<(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return (a.curr < b.curr);
}
template <typename T>
bool operator>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return (b < a);
}
template <typename T>
bool operator<=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return !(a > b);
}
template <typename T>
bool operator>=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
    return !(a < b);
}

int main(int argc, char const *argv[])
{
    try
    {
        std::string hello = "hello";
        Blob<std::string> blob_s({"1"});
        blob_s.pop_back();
        // 触发check 异常
        //blob_s.pop_back();
        blob_s.push_back("2");
        blob_s.push_back(hello);
        //
        std::cout << blob_s.size() << std::endl;
        blob_s.print(std::cout);
        std::cout << blob_s.back() << std::endl;
        std::cout << blob_s[blob_s.size() - 1] << std::endl;

        // 测试 iterator
        BlobPtr<std::string> blob_pt1(blob_s);
        std::cout << blob_pt1[1] << std::endl;

        // 测试迭代器
        std::cout << "By iterator" << std::endl;
        for (auto b = blob_s.begin(); b != blob_s.end(); b++)
        {
            std::cout << *b << std::endl;
        }
    }
    catch (const std::exception &msg)
    {
        std::cout << msg.what() << std::endl;
    }

    {
        Blob<std::string> sb1{"a", "b", "c"};
        Blob<std::string> sb2 = sb1;

        sb2[2] = "b";

        if (sb1 > sb2)
        {
            for (auto iter = sb2.cbegin(); iter != sb2.cend(); ++iter)
                std::cout << *iter << " ";
            std::cout << std::endl;
        }

        ConstBlobPtr<std::string> iter(sb2);
        std::cout << iter->size() << std::endl;
    }

    while (1)
        ;
    return 0;
}

16.13

// 16.3 解释你为BlobPtr的相等和关系运算符选择那种类型的友好关系

模版参数T 一直的 重载都为其友元关系

16.14

16.15

// 16.14 编写Screen类模版,用非类型参数定义Screen的高和宽,可以当宏使用
#include <string>
#include <algorithm>
#include <iostream>

//using pos = int;
using pos = std::string::size_type;

template <pos, pos>
class Screen;

template <pos H, pos W>
std::istream &operator>>(std::istream &, Screen<H, W> &);

template <pos H, pos W>
std::ostream &operator<<(std::ostream &, const Screen<H, W> &);

template <pos H, pos W>
class Screen
{

    friend std::istream &operator>><H, W>(std::istream &, Screen<H, W> &);
    friend std::ostream &operator<<<H, W>(std::ostream &, const Screen<H, W> &);

private:
    pos curr = 0;
    std::string contents;

public:
    Screen() = default;
    Screen(char c) : contents(H * W, c) {}
    char get() const { return contents[curr]; }
    char get(pos a, pos b) const { return contents[a * W + b]; }
    Screen &set(char c)
    {
        contents[curr++] = c;
        curr = std::min(curr, H * W);
        return *this;
    }
    Screen &set(pos a, pos b, char c)
    {
        contents[a * W + b] = c;
        return *this;
    }
    Screen &move(pos a, pos b);
};

template <pos H, pos W>
Screen<H, W> &Screen<H, W>::move(pos a, pos b)
{
    curr = a * W + b;
    return *this;
}

template <pos H, pos W>
std::istream &operator>>(std::istream &is, Screen<H, W> &s)
{
    std::string input;
    is >> input;
    for (char ch : input)
        s.set(ch);
    return is;
}
template <pos H, pos W>
std::ostream &operator<<(std::ostream &os, const Screen<H, W> &s)
{
    for (pos r = 0; r != H; ++r)
    {
        for (pos c = 0; c != W; ++c)
        {
            os << s.get(r, c);
        }
        os << std::endl;
    }
    return os;
}

int main()
{
    Screen<5, 5> screen('x');
    screen.set(2, 2, 'o');
    std::cout << screen << std::endl;

    std::cout << "please input some characters as you like:";
    std::cin >> screen;
    std::cout << screen << std::endl;
    while (1)
        ;
    {
        /* code */
    }
}

// xxxxx
// xxxxx
// xxoxx
// xxxxx
// xxxxx

// please input some characters as you like:1234512345123451234512345999
// 12345
// 12345
// 12345
// 12345
// 12345

16.17

在定义模板参数类型的时候,typename 和class 没什么不同
在 有些时候需要使用 模板类的类型的时候,需要加上typename,因为默认识别为类的成员 p593

T::x a;  T是模板,有两种理解
1. x是静态成员,与a相*------默认是这个
2. 定义一个a变量,类型是T::x


16.18

// 解释下面的代码是否合法
> (a) template <typename T, U, typename V> void f1(T, U, V);
> (b) template <typename T> T f2(int &T);
> (c) inline template <typename T> T foo(T, unsigned int*);
> (d) template <typename T> f4(T, T);
> (e) typedef char Ctype;
> template <typename Ctype> Ctype f5(Ctype a);

a. template <typename T, typename U, typename V> void f1(T, U, V);
b. template <typename T> T f2(int &a);	// typename 被隐藏
c. template <typename T> inline T foo(T, unsigned int*); //inline的位置
d. template <typename T> void f4(T, T); // 提供返回类型
e. 隐藏了typedef
    

16.19

16.20

// 16.19 接受容器的引用,打印容器的元素.使用 size_type 和size 控制打印
// 16.20 使用迭代器
#include <iostream>
#include <vector>
#include <list>
#include <string>
using namespace std;

template <typename T>
printv(const T &v)
{
    for (typename T::size_type i = 0; i < v.size(); i++)
        cout << v.at(i) << "<>";
    cout << endl;
}

template <typename T>
printv2(const T &v)
{
    for (auto c = v.begin(); c != v.end(); c++)
        cout << *c << "<>";
    cout << endl;
}

int main(int argc, char const *argv[])
{
    vector<int> v1({1, 2, 3, 4, 5, 6, 7});
    printv(v1);

    //因为printv使用的是 size 不是迭代器,所以不能用list
    list<string> v2({"123", "456"});
    printv2(v2);

    while (1)
        ;
    return 0;
}

16.21

// 16.21 实现自己的DebugDelete版本
#include <iostream>
#include <memory>

using namespace std;

class DebugDelete
{
private:
    std::ostream &dbgos;

public:
    DebugDelete(std::ostream &o = std::cout) : dbgos(o) {}
    template <typename T>
    void operator()(T *elem) const
    {
        dbgos << "delete by DebugDelete" << std::endl;
        delete elem;
    }
};

int main(int argc, const char **argv)
{
    DebugDelete d;
    int *a = new int;
    d(a);

    int *b = new int;
    DebugDelete()(b);

    {

        std::unique_ptr<int, DebugDelete> ui(new int, DebugDelete());
        // ui.release(); 释放ui但保留内存
        // ui.reset();   释放ui内存的东西,会调用DebugDelete
        std::cout << "/* message */" << std::endl;
    }
    while (1)
        ;
    return 0;
}

16.24

// 16.24 为你的Blob添加一个构造函数支持两个迭代器

// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员

// 16.24
template <typename type_it>
Blob(type_it a, type_it b) : data(std::make_shared<std::vector<T>>(a, b)) {}

try
{
    std::list<std::string> ls({"A1", "A2", "A3"});
    Blob<std::string> blob_s(ls.begin(), ls.end());
    blob_s.print(std::cout);
}
catch (const std::exception &e)
{
    std::cerr << e.what() << '
';
}

16.25

// 16.25 解释下面的语句

// 显式实例化,需要在其他的地方定义
extern template class vector<string>;
// 定义
template class vector<Sales_data>;

16.26

// 16.26 假设Nodefault 是一个没有默认构造函数的类,我们可以显式实例化 vecctor<NoDefault> 吗?

不能,因为vector需要调用默认的构造函数

16.27

// 16.27 对于下面带标签的语句,解释发生什么样的实例化?(如果有的话)
// 		 如果一个模版被实例化,解释为什么,如果没有,解释为什么没有

template <typename T> class Stack { };

void f1(Stack<char>);                   // (a)
class Exercise {
    Stack<double> &rsd;                 // (b)
    Stack<int>    si;                   // (c)
};
int main() {
    Stack<char> *sc;                    // (d)
    f1(*sc);                            // (e)
    int iObj = sizeof(Stack< string >); // (f)
}

/*
a. 没有,只是一个声明语句,当调用的时候实例化
b. 没有,这里只是创建一个指针,不需要实例化
c  有,这是一个非模板的类,创造对象
d  没有,指针和引用不需要
e  有   Stack<char>
f  编译阶段实例化,但是只是临时的应该

*/

// 测试方法
// Solution from [How is a template instantiated? - Stack Overflow](https://stackoverflow.com/questions/21598635/how-is-a-template-instantiated)

#include <iostream>
using namespace std;


template <typename T> class Stack {
  typedef typename T::ThisDoesntExist StaticAssert; // T::NotExisting doesn't exist at all!
};


void f1(Stack<char>); // No instantiation, compiles

class Exercise {
  Stack<double> &rsd; // No instantiation, compiles (references don't need instantiation, are similar to pointers in this)

  Stack<int>    si; // Instantiation! Doesn't compile!!
};


int main(){

  Stack<char> *sc; // No Instantiation, this compiles successfully since a pointer doesn't need instantiation

  f1(*sc); // Instantiation of Stack<char>! Doesn't compile!!

  int iObj = sizeof(Stack< std::string >); // Instantiation of Stack<std::string>, doesn't compile!!

}

16.28

// 16.28 编写自己的shared_ptr  和 unique_ptr
// shared_ptr  保存一个指针,参数为value type*  按照delete是返回void的函数  using DelFuncPtr = void (*)(T*);
// unique_ptr 保存的是删除器的类,因为不会被改变

#include <vector>
#include <string>
#include <iostream>

//*********************************************************************************************************
/*
    0. 参考声明 template< class T > class shared_ptr;
    1. 内容指针,引用计数指针,删除器的函数指针
    2. 构造函数参考,先实现默认构造,即带一个指针和一个删除器
    3. 拷贝构造
    4. 拷贝赋值,这里需要释放原来的空间,可以使用 swap 传递值的方式 来实现
    5. 所以我们先实现swap   void swap( shared_ptr& r ) noexcept;
    6. 析构函数,判断引用,判断是否nullptr
    7. void reset( Y* ptr, Deleter d );
*/
template <class T>
class SharedPtr
{
    using DelFuncPtr = void (*)(T *);

private:
    T *ptr_ = nullptr;
    size_t *count_ptr_ = nullptr;
    DelFuncPtr del_ = nullptr;

public:
    SharedPtr(T *ptr = nullptr, DelFuncPtr del = nullptr) : ptr_(ptr), del_(del), count_ptr_(new size_t(ptr_ != nullptr)) {}
    SharedPtr(const SharedPtr &s) : ptr_(s.ptr_), del_(s.del_), count_ptr_(s.count_ptr_)
    {
        ++*s.count_ptr_;
    }
    SharedPtr &operator=(SharedPtr s) //这里使用值传递,会先复制一份
    {
        //交换自己和临时的,临时的在退出的时候会自动调用析构释放
        swap(s);
        return *this;
    }

    void reset(T *ptr = nullptr, DelFuncPtr d = nullptr)
    {
        auto news = SharedPtr(ptr, d);
        swap(news);
    }

    void swap(SharedPtr &r) noexcept
    {
        using std::swap;
        swap(ptr_, r.ptr_);
        swap(count_ptr_, r.count_ptr_);
        swap(del_, r.del_);
    }
    ~SharedPtr()
    {
        if (ptr_ == nullptr)
            return;
        if (0 == --*count_ptr_)
        {
            del_ == nullptr ? delete (ptr_) : del_(ptr_);
            delete count_ptr_;
        }
        ptr_ = nullptr;
        count_ptr_ = nullptr;
    }

    T *get() const noexcept
    {
        return ptr_;
    }

    size_t use_count() const noexcept
    {
        return *count_ptr_;
    }

    T &operator*() const noexcept
    {
        return *ptr_;
    }
    T *operator->() const noexcept
    {
        return ptr_;
    }
    //检查 *this 是否存储非空指针,即是否有 get() != nullptr
    explicit operator bool() const noexcept
    {
        return get() != nullptr;
    }
};

//*********************************************************************************************************
/**unique_ptr 保存的是删除器的类,因为不会被改变
 */

class Delete
{
public:
    template <typename T>
    void operator()(T *ptr) const { delete ptr; }
};

template <typename T, typename D = Delete>
class UniquePtr
{
private:
    T *ptr_ = nullptr;
    D del_;

public:
    UniquePtr(T *ptr = nullptr, const D &d = D()) : ptr_(ptr), del_(d) {}
    ~UniquePtr() { del_(ptr_); }
    UniquePtr(const UniquePtr &) = delete;
    UniquePtr &operator=(const UniquePtr &) = delete;

    UniquePtr(UniquePtr &&other) noexcept : ptr_(other.ptr_), del_(std::move(other.del_))
    {
        other.ptr_ = nullptr;
    }

    UniquePtr &operator=(UniquePtr &&other) noexcept
    {
        if (this != &other)
        {
            reset();
            ptr_ = other.ptr_;
            del_ = std::move(other.del_);
            other.ptr_ = nullptr;
        }
        return *this;
    }
    UniquePtr &operator=(std::nullptr_t) noexcept
    {
        reset();
        return *this;
    }
    T *release() noexcept
    {
        T *ret = ptr_;
        ptr_ = nullptr;
        return ret;
    }

    void reset(T *ptr = nullptr)
    {
        del_(ptr_);
        ptr_ = ptr;
    }

    void swap(UniquePtr &other) noexcept
    {
        using std::swap;
        swap(ptr_, other.ptr_);
        swap(del_, other.del_);
    }

    T *get() const noexcept
    {
        return ptr_;
    }

    D &get_deleter() noexcept { return del_; }
    const D &get_deleter() const noexcept { return del_; }

    T &operator*() const noexcept
    {
        return *ptr_;
    }
    T *operator->() const noexcept
    {
        return ptr_;
    }
    T &operator[](size_t i) const { return ptr_[i]; }
    //检查 *this 是否存储非空指针,即是否有 get() != nullptr
    explicit operator bool() const noexcept
    {
        return get() != nullptr;
    }
};
//*********************************************************************************************************
class DebugDelete
{
private:
    std::ostream &dbgos;

public:
    DebugDelete(std::ostream &o = std::cout) : dbgos(o) {}
    template <typename T>
    void operator()(T *elem) const
    {
        dbgos << "delete by DebugDelete" << std::endl;
        delete elem;
    }
};

int main(int argc, const char **argv)
{
    {
        using std::string;
        using std::vector;
        DebugDelete d;

        string *mystring = new string("123456");

        //SharedPtr<string> myshared_string(mystring, [](string *p) {std::cout << "Call delete from lambda...
";delete p; });
        SharedPtr<string> myshared_string(mystring, [](string *p) { DebugDelete()(p); });
        std::cout << *myshared_string.get() << std::endl;

        std::cout << "Test Copy assiment" << std::endl;
        SharedPtr<string> myshared_string3(new string("55555"));
        myshared_string3 = myshared_string;

        std::cout << "Test Copy Construct" << std::endl;
        SharedPtr<string> myshared_string2(myshared_string);
        std::cout << myshared_string.use_count() << std::endl;
    }

    {
        using std::string;
        using std::vector;
        std::cout << "Test Unique Ptr" << std::endl;
        UniquePtr<string> s1(new string("A10086"));
        std::cout << *s1.get() << std::endl;

        UniquePtr<string, DebugDelete> s2(new string("B10086"), DebugDelete());
        std::cout << *s1.get() << std::endl;
    }
    while (1)
        ;
    return 0;
}

16.29

16.30

// 修改你的Blob类,用你的shared_ptr 替代标准库的版本
// 这里不能使用weak_ptr了,应该自己再做一个,
// 在赋值迭代器的时候,weak_ptr 的赋值操作没有对 我们自己实现的 重载
// 这里我们修改Blob 不使用迭代器了
// 16.24 为你的Blob添加一个构造函数支持两个迭代器

// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员

#include <vector>
#include <memory>
#include <string>
#include <list>
#include <iostream>
#include <initializer_list>
#include <exception>
#include <algorithm>

template <class T>
class SharedPtr
{
    using DelFuncPtr = void (*)(T *);

private:
    T *ptr_ = nullptr;
    size_t *count_ptr_ = nullptr;
    DelFuncPtr del_ = nullptr;

public:
    SharedPtr(T *ptr = nullptr, DelFuncPtr del = nullptr) : ptr_(ptr), del_(del), count_ptr_(new size_t(ptr_ != nullptr)) {}
    SharedPtr(const SharedPtr &s) : ptr_(s.ptr_), del_(s.del_), count_ptr_(s.count_ptr_)
    {
        ++*s.count_ptr_;
    }
    SharedPtr &operator=(SharedPtr s) //这里使用值传递,会先复制一份
    {
        //交换自己和临时的,临时的在退出的时候会自动调用析构释放
        swap(s);
        return *this;
    }

    void reset(T *ptr = nullptr, DelFuncPtr d = nullptr)
    {
        auto news = SharedPtr(ptr, d);
        swap(news);
    }

    void swap(SharedPtr &r) noexcept
    {
        using std::swap;
        swap(ptr_, r.ptr_);
        swap(count_ptr_, r.count_ptr_);
        swap(del_, r.del_);
    }
    ~SharedPtr()
    {
        if (ptr_ == nullptr)
            return;
        if (0 == --*count_ptr_)
        {
            del_ == nullptr ? delete (ptr_) : del_(ptr_);
            delete count_ptr_;
        }
        ptr_ = nullptr;
        count_ptr_ = nullptr;
    }

    T *get() const noexcept
    {
        return ptr_;
    }

    size_t use_count() const noexcept
    {
        return *count_ptr_;
    }

    T &operator*() const noexcept
    {
        return *ptr_;
    }
    T *operator->() const noexcept
    {
        return ptr_;
    }
    //检查 *this 是否存储非空指针,即是否有 get() != nullptr
    explicit operator bool() const noexcept
    {
        return get() != nullptr;
    }
};

template <typename T>
class Blob;

template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b);

template <typename T>
class Blob
{

    friend bool operator==<T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator!=<T>(const Blob<T> &a, const Blob<T> &b);
    // clang-format off
    friend bool operator< <T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator> <T>(const Blob<T> &a, const Blob<T> &b);
    // clang-format on
    friend bool operator<=<T>(const Blob<T> &a, const Blob<T> &b);
    friend bool operator>=<T>(const Blob<T> &a, const Blob<T> &b);

    // 以下两个应该是为了外部方便获取类型
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;

private:
    SharedPtr<std::vector<T>> data;
    void check(size_type i, const std::string &msg) const;

public:
    Blob() : data(new std::vector<T>()) {}
    // 16.24
    template <typename type_it>
    Blob(type_it a, type_it b) : data(new std::vector<T>(a, b)) {}

    Blob(const Blob<T> &s) : data(new std::vector<T>(*s.data)) {}
    Blob(Blob<T> &&a) noexcept : data(std::move(a.data)) {}

    Blob &operator=(const Blob<T> &s);
    Blob &operator=(Blob<T> &&) noexcept;

    Blob(std::initializer_list<T> il) : data(new std::vector<T>(il)) {}
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    void push_back(T &val) { data->push_back(val); };
    void push_back(const T &val) { data->push_back(std::move(val)); }
    void pop_back();

    T &back();
    const T &back() const;
    T &front();
    const T &front() const;

    T &operator[](size_type i);
    const T &operator[](size_type i) const;

    void print(std::ostream &o);
};

template <typename T>
Blob<T> &Blob<T>::operator=(const Blob<T> &s)
{
    data = new std::vector<T>(*s.data);
    return *this;
}

template <typename T>
Blob<T> &Blob<T>::operator=(Blob<T> &&s) noexcept
{
    if (this != s)
    {
        data = std::move(s.data);
        s.data = nullptr;
    }
    return *this;
}

template <typename T>
void Blob<T>::print(std::ostream &o)
{
    for (auto ch : (*data))
        o << ch << ",";
    o << std::endl;
}

template <typename T>
void Blob<T>::check(size_type i, const std::string &msg) const
{
    if (i >= size())
    {
        throw std::out_of_range(msg);
    }
}

template <typename T>
void Blob<T>::pop_back()
{
    check(0, "empty to pop_back");
    data->pop_back();
}

template <typename T>
T &Blob<T>::back()
{
    check(0, "Get empty Back()");
    return data->back();
}
template <typename T>
const T &Blob<T>::back() const
{
    check(0, "Get empty Back()");
    return data->back();
}

template <typename T>
T &Blob<T>::front()
{
    check(0, "Get empty front()");
    return data->front();
}
template <typename T>
const T &Blob<T>::front() const
{
    check(0, "Get empty front()");
    return data->front();
}

template <typename T>
T &Blob<T>::operator[](size_type i)
{
    check(i, "Get [" + std::to_string(i) + "]");
    return data->at(i);
}

template <typename T>
const T &Blob<T>::operator[](size_type i) const
{
    check(i, "Get [" + std::to_string(i) + "]");
    return data->at(i);
}

//****************************************************************************
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b)
{
    return (*a.data == *b.data);
}
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b)
{
    return !(a == b);
}

template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b)
{
    return std::lexicographical_compare(a.data->begin(), a.data->end(), b.data->begin(), b.data->end());
}

template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b)
{
    return (b < a);
}
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b)
{
    return !(b > a);
}
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b)
{
    return !(a < b);
}

int main(int argc, const char **argv)
{
    Blob<std::string> blob_s({"1", "2"});
    blob_s.print(std::cout);
    while (1)
        ;
    return 0;
}

16.31

// 16.31 如果我们将DebugDelete 与 unique_ptr 一起使用,解释编译器将删除器处理为内联形式的可能?
// The compiler will set the default deleter type as `DebugDelete`, 
// which will be executed is known at compile time.
编译的时候就传递赋值

16.32

// 在模版实参推断过程中发生了什么?
1. 类型推导
2. 实例化函数

16.33

指出在模版实参推断过程中允许对函数实参进行的两种类型转换。
1. const转换, 实参是非const的传递给形参是const的
2. 数组或函数转换为指针,注意:形参不能是引用,形参是引用,传递的是数组,含有纬度的

16.34

对下面的代码解释每个调用是否合法。如果合法,T 的类型是什么?如果不合法,为什么?
template <class T> int compare(const T&, const T&);
(a) compare("hi", "world");
(b) compare("bye", "dad");

a. 不合法,这里传递的是char(&a)[3] char(&a)[6]
b. 合法

13.35

13.35 下面调用中哪些是错误的(如果有的话)?如果调用合法,T 的类型是什么?如果调用不合法,问题何在?


template <typename T> T calc(T, int);
tempalte <typename T> T fcn(T, T);
double d; float f; char c;
(a) calc(c, 'c'); 
(b) calc(d, f);
(c) fcn(c, 'c');
(d) fcn(d, f);

a. 合法, T=char
b. 合法,T=double
c  合法 char,
d  不合法,不能发生类型转换

16.36

// 进行下面的调用会发生什么
template <typename T> f1(T, T);
template <typename T1, typename T2) f2(T1, T2);
int i = 0, j = 42, *p1 = &i, *p2 = &j;
const int *cp1 = &i, *cp2 = &j;
(a) f1(p1, p2);
(b) f2(p1, p2);
(c) f1(cp1, cp2);
(d) f2(cp1, cp2);
(e) f1(p1, cp1);
(f) f2(p1, cp1);

// f1没有返回值  哈哈哈
a. f1(int*,int*);
b. f2(int*,int*);
c. f1(const int*,const int*);
d. f2(const int*,const int*);
e. error
f. f2( int*,const int*);

16.37

标准库 max 函数有两个参数,它返回实参中的较大者。此函数有一个模版类型参数。
你能在调用 max 时传递给它一个 int 和一个 double 吗?如果可以,如何做?如果不可以,为什么?



指定参数类型 max<double>(a, b);

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main(int argc, char const *argv[])
{
    int a = 6;
    double b = 6.1231;
    std::cout << std::max<long double>(a, b) << std::endl;
    // initializer_list<T> 也是模版,不会发生转换
    //std::cout << std::max({a, b}, [](const int a, const int b) { return a < b; }) << std::endl;
    std::cout << std::max({1, 2}, [](const int a, const int b) { return a < b; }) << std::endl;

    while (1)
        ;
    return 0;
}

16.38

当我们调用 make_shared 时,必须提供一个显示模版实参。解释为什么需要显式模版实参以及它是如果使用的。

返回类型无法推断

16.39

对16.1.1节 578 中的原始版本的 compare 函数,使用一个显式模版实参,使得可以向函数传递两个字符串字面量。
compare<std::string>("a", "bb");

16.40

下面的函数是否合法?如果不合法,为什么?如果合法,对可以传递的实参类型有什么限制(如果有的话)?
返回类型是什么?

合法,元素支持+ 返回类型由operator+决定
template <typename It>
auto fcn3(It beg, It end) -> decltype(*beg + 0)
{
	//处理序列
	return *beg;
}

16.41

编写一个新的 sum 版本,它返回类型保证足够大,足以容纳加法结果

template <typename T1, typename T2>
auto sum(T1 a, T2 b) -> decltype(a + b) {
    return a + b;
}

16.42

// 对下面每个调用,确定 T 和 val 的类型:
template <typename T> void g(T&& val);
int i = 0; const int ci = i;
(a) g(i);
(b) g(ci);
(c) g(i * ci);

a. i是左值,所以会传递左值引用 int& && 折叠为 int&
b. ci 是const int, 折叠为const int&
c. i*ci是一个局部的变量,是个右值int&& 折叠为int&&

16.43

使用上一题定义的函数,如果我们调用g(i = ci),g 的模版参数将是什么?
i=ci 返回的是 i 也就是 int,折叠为int&

#include <iostream>

int main(int argc, char const *argv[])
{
    int i = 0;
    ++(i = 3);
    std::cout << i << std::endl;	//4

    while (1)
        ;
    return 0;
}

16.44

使用与第一题中相同的三个调用,如果 g 的函数参数声明为 T(而不是T&&),确定T的类型。
如果g的函数参数是 const T&呢?


template <typename T> void g(T val);
int i = 0; const int ci = i;
(a) g(i);    		int
(b) g(ci);   		int 
(c) g(i * ci);		int


template <typename T> void g(const& val);
int i = 0; const int ci = i;
(a) g(i);    		 int 
(b) g(ci);   		 int 
(c) g(i * ci);		 int 


16.45

如果下面的模版,如果我们对一个像42这样的字面常量调用g,解释会发生什么?
如果我们对一个int 类型的变量调用g 呢?

template <typename T> void g(T&& val) { vector<T> v; }

g(42)	传递的就是 int&& 
{
    vector<int&&> v;
}

int i;
g(i) // 传递是 int& && 折叠为 int&
{
    vector<int&> v; //报错,无法保存引用
}

16.46

// 解释下面的循环,它来自13.5节中的 StrVec::reallocate:
// alloc.construct(dest++, std::move(*elem++));

// *elem 为左值,转换为 elem的右值
template< class U, class... Args >
void construct( U* p, Args&&... args );

16.47

#include <iostream>

void f(int v1, int &v2)
{
    std::cout << v1 << " " << ++v2 << std::endl;
}

void g(int &&i, int &j)
{
    std::cout << i << " " << ++j << std::endl;
}

template <typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
    f(std::forward<T2>(t2), std::forward<T1>(t1));
}

int main()
{
    int j = 0;
    flip(f, j, 42);
    flip(g, j, 42);
    while (1)
        ;
}

16.48

// 16.48 编写你自己版本的 debug_rep 函数。

#include <iostream>
#include <string>
#include <sstream>
using std::string;

template <typename T>
string debug_rep(const T &t)
{
    std::ostringstream ret;
    ret << t;
    return ret.str();
}

template <typename T>
string debug_rep(T *t)
{
    std::ostringstream ret;
    ret << "Point Addr=" << t;
    if (t == nullptr)
        ret << "nullptr";
    else
        ret << debug_rep(*t);
    return ret.str();
}

string debug_rep(const string &t)
{
    return "String:" + t;
}

string debug_rep(char *t)
{
    return "char*" + debug_rep(string(t));
}

string debug_rep(const char *t)
{
    return "const char*" + debug_rep(string(t));
}

int main(int argc, const char **argv)
{

    std::cout << debug_rep("123456") << std::endl;
    while (1)
        ;

    return 0;
}

16.49

//16.49解释下面每个调用会发生什么
template <typename T> void f(T);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(T*);
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42); g(p); g(ci); g(p2);
f(42); f(p); f(ci); f(p2);


g(42)   T=int  			3.template <typename T> void g(T);
g(p) 	T=int			4.template <typename T> void g(T*);
g(ci)	T=const int		3.template <typename T> void g(T);
g(p2)	T=const int		4.template <typename T> void g(T*);

f(42)	T=int			1.template <typename T> void f(T);
f(p)	T=int*			1.template <typename T> void f(T);
f(ci)	T=const int		1.template <typename T> void f(T);
f(p2)   T=int			2.template <typename T> void f(const T*);

16.50

// 16.50 定义上一个练习中的函数,令它们打印一条身份信息。运行该练习中的代码。如果函数调用的行为与你预期不符,确定你理解了原因。
/*
template <typename T> void f(T);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(T*);
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42); g(p); g(ci); g(p2);
f(42); f(p); f(ci); f(p2);


g(42)   T=int  			3.template <typename T> void g(T);
g(p) 	T=int			4.template <typename T> void g(T*);
g(ci)	T=const int		3.template <typename T> void g(T);
g(p2)	T=const int		4.template <typename T> void g(T*);

f(42)	T=int			1.template <typename T> void f(T);
f(p)	T=int*			1.template <typename T> void f(T);
f(ci)	T=const int		1.template <typename T> void f(T);
f(p2)   T=int			2.template <typename T> void f(const T*);
*/
#include <iostream>
#include <string>

template <typename T>
void f(T)
{
    std::cout << "1" << std::endl;
}
template <typename T>
void f(const T *)
{
    std::cout << "2" << std::endl;
}
template <typename T>
void g(T)
{
    std::cout << "3" << std::endl;
}
template <typename T>
void g(T *)
{
    std::cout << "4" << std::endl;
}

int main(int argc, char const *argv[])
{
    int i = 42, *p = &i;
    const int ci = 0, *p2 = &ci;
    g(42);
    g(p);
    g(ci);
    g(p2);
    f(42);
    f(p);
    f(ci);
    f(p2);

    while (1)
        ;
    return 0;
}

16.51

16.52

// 16.51 调用本节中的每个 foo,确定 sizeof…(Args) 和 sizeof…(rest)分别返回什么。
#include <iostream>
using namespace std;

template <typename T, typename... Args>
void foo(const T &t, const Args &... rest)
{
    std::cout << "sizeof...(Args)=" << sizeof...(Args) << "  sizeof...(rest)=" << sizeof...(rest) << std::endl;
}

int main(int argc, const char **argv)
{
    int i = 0;
    double d = 3.14;
    string s = "how";

    foo(i, s, 42, d);       /// sizeof...(Args)=3  sizeof...(rest)=3
    foo(s, 42, "hi");       /// sizeof...(Args)=2  sizeof...(rest)=2
    foo(d, s);              /// sizeof...(Args)=1  sizeof...(rest)=1
    foo("hi");              /// sizeof...(Args)=0  sizeof...(rest)=0
    foo(i, s, s, d);        /// sizeof...(Args)=3  sizeof...(rest)=3

    while (1)
        ;
    return 0;
}

16.53

// 16.53 编写你自己版本的 print 函数,并打印一个、两个及五个实参来测试它,要打印的每个实参都应有不同的类型。

#include <iostream>
using namespace std;

template <typename T>
ostream &print(ostream &o, const T &s)
{
    return o << s << endl;
}

template <typename A, typename... T>
ostream &print(ostream &o, const A &a, const T &... s)
{
    o << a << ",";
    return print(o, s...);
}

int main(int argc, const char **argv)
{
    int i = 1;
    double d = 3.14;
    float f = 99.99;
    string s = "hello";

    print(std::cout, i);
    print(std::cout, i, s);
    print(std::cout, i, d, f, s, "123456");

    while (1)
        ;

    return 0;
}

16.54

如果我们对一个没 << 运算符的类型调用 print,会发生什么?
编译实例化失败.
    //no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'const std::vector<int>')
    //print(std::cout, i, d, f, s, vector<int>(1));

16.55

// 如果我们的可变参数版本 print 的定义之后声明非可变参数版本,解释可变参数的版本会如何执行。
我们最终会执行到只有 ostream,没有第二个参数 print(std::ostream&)
但是我们的模版函数是 ostream &print(ostream &o, const A &a, const T &... s)
也就是没有匹配一个参数的版本

16.56

// 16.56 编写并测试可变参数版本的 errorMsg。
#include <initializer_list>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

/*********************************************************************/
template <typename T>
string debug_rep(const T &t)
{
    std::ostringstream ret;
    ret << t;
    return ret.str();
}

template <typename T>
string debug_rep(T *t)
{
    std::ostringstream ret;
    ret << "Point Addr=" << t;
    if (t == nullptr)
        ret << "nullptr";
    else
        ret << debug_rep(*t);
    return ret.str();
}

string debug_rep(const string &t)
{
    return "String:" + t;
}

string debug_rep(char *t)
{
    return "char*" + debug_rep(string(t));
}

string debug_rep(const char *t)
{
    return "const char*" + debug_rep(string(t));
}

/*********************************************************************/

void err_msg_old(initializer_list<string> il)
{
    for (auto ch : il)
        std::cout << ch << ",";
    std::cout << std::endl;
}

template <typename T>
ostream &print(ostream &o, const T &s)
{
    return o << s << endl;
}
template <typename A, typename... T>
ostream &print(ostream &o, const A &a, const T &... s)
{
    o << a << ",";
    return print(o, s...);
}

template <typename... T>
ostream &err_msg(ostream &o = std::cout, const T &... s)
{
    // 这里会展开  debug_rep(int) debug_rep(char)
    return print(o, debug_rep(s)...);
}

int main(int argc, char const *argv[])
{
    err_msg_old({"1", "2", "3"});
    err_msg(std::cout, "1", 1, 3.14);
    while (1)
        ;
    return 0;
}

16.57

可变参数的比较灵活,但是要用比较多的堆栈估计
简单版本的,代码简单,方便调试

16.58

// 16.58 你的 StrVec 类及你为16.1.2节练习中编写的 Vec 类添加 emplace_back 函数。
// template< class... Args >
// void emplace_back( Args&&... args );

/*
    template <class... Args>
    T *emplace_back(Args &&... args)
    {
        chk_n_alloc();
        alloc.construct(first_free++, std::forward<Args>(args)...);
    }

    std::string its = "my_name_is_yer";
    vec.emplace_back(its.begin(), its.end());
    vec.emplace_back("back_123");
    vec.emplace_back(20, 'c');

*/

// 16.16 定义模版类的vec 替代之前的Strvec

#include <memory>
#include <algorithm>
#include <utility>
#include <initializer_list>
#include <exception>
#include <string>
#include <stdexcept>
#include <iostream>
// using std::cin;
// using std::cout;
// using std::endl;

// 定义一些公共操作
// 从一个迭代器范围复制 到指定的位置,返回内存的首地址和尾地址+1 也就是返回 begin和end

/**
 如何设计一个 vector
 1. 首先我们定义一个分配器,一个首地址,尾地址,以及容量
 2. 提供 size,begin,end,capacity,at,[]
 3. push_back
        chk_n_alloc();
        3.1 检查容量的操作
        3.2 容量的分配,这里需要设计一个移动函数    
        alloc.construct
        3.3 数据构造 
 4. 移动函数涉及
        alloc_n_move
        4.1 内存分配
        4.2 原来数据的移动
 5.设计reserve
        5.1 内存分配和移动 alloc_n_move
 6.设计resize
    重设容器大小以容纳 count 个元素。

    若当前大小大于 count ,则减小容器为其首 count 个元素。
    设置指针,调用元素的析构函数 destroy析构在已分配存储中的对象 

    若当前大小小于 count ,则后附额外元素,并以 value 的副本初始化。
    也就是调用reserve


*/

template <typename>
class Vec;

template <typename T>
bool operator==(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator!=(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator<(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator>(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator<=(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator>=(const Vec<T> &, const Vec<T> &);

template <typename T>
class Vec
{
    friend bool operator==<T>(const Vec<T> &, const Vec<T> &);
    friend bool operator!=<T>(const Vec<T> &, const Vec<T> &);
    // clang-format off
    friend bool operator< <T>(const Vec<T>&, const Vec<T>&);
    friend bool operator> <T>(const Vec<T>&, const Vec<T>&);
    // clang-format on
    friend bool operator<=<T>(const Vec<T> &, const Vec<T> &);
    friend bool operator>=<T>(const Vec<T> &, const Vec<T> &);

private:
    T *elements;
    T *first_free;
    T *cap;
    std::allocator<T> alloc;

    std::pair<T *, T *> alloc_n_copy(const T *b, const T *e);
    void range_initialize(const T *first, const T *last);
    void free();
    void reallocate();
    void alloc_n_move(size_t new_cap);
    void chk_n_alloc()
    {
        if (size() == capacity())
            reallocate();
    }

public:
    Vec(std::initializer_list<T>);
    Vec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
    Vec(const Vec<T> &);
    //------------------------------------------------------------Vec<T> &operator=(const Vec<T> &s);
    Vec &operator=(const Vec<T> &s);
    Vec(Vec<T> &&) noexcept;
    Vec &operator=(Vec<T> &&) noexcept;

    T *begin() const { return elements; }
    T *end() const { return first_free; }
    T &at(size_t pos) { return *(elements + pos); }
    const T &at(size_t pos) const { return *(elements + pos); }

    T &operator[](size_t n) { return elements[n]; }
    const T &operator[](size_t n) const { return elements[n]; }

    void push_back(const T &);
    size_t size() const { return first_free - elements; }
    size_t capacity() const { return cap - elements; }

    void reserve(size_t new_cap);
    void resize(size_t count);
    void resize(size_t count, const T &);

    template <class... Args>
    T *emplace_back(Args &&... args)
    {
        chk_n_alloc();
        alloc.construct(first_free++, std::forward<Args>(args)...);
    }

    ~Vec();
};

template <typename T>
std::pair<T *, T *> Vec<T>::alloc_n_copy(const T *b, const T *e)
{
    auto data = alloc.allocate(e - b);
    return {data, std::uninitialized_copy(b, e, data)};
}

template <typename T>
void Vec<T>::range_initialize(const T *first, const T *last)
{
    auto newdata = alloc_n_copy(first, last);
    elements = newdata.first;
    first_free = cap = newdata.second;
}
template <typename T>
void Vec<T>::free()
{
    if (elements)
    {
        //执行元素的析构函数
        for_each(elements, first_free, [this](T &rhs) { alloc.destroy(&rhs); });
        //释放vector内存
        alloc.deallocate(elements, cap - elements);
    }
}

template <typename T>
Vec<T>::Vec(std::initializer_list<T> il)
{
    range_initialize(il.begin(), il.end());
}
template <typename T>
Vec<T>::Vec(const Vec<T> &s)
{
    range_initialize(s.begin, s.end);
}

template <typename T>
Vec<T> &Vec<T>::operator=(const Vec<T> &s)
{
    if (this != s)
    {
        auto data = alloc_n_copy(s.begin(), s.end());
        free();
        elements = data.first;
        first_free = cap = data.second;
    }
    return *this;
}
template <typename T>
Vec<T>::Vec(Vec<T> &&s) noexcept
{
    elements = s.elements;
    first_free = s.first_free;
    cap = s.cap;
    s.elements = s.first_free = s.cap = nullptr;
}
template <typename T>
Vec<T> &Vec<T>::operator=(Vec<T> &&s) noexcept
{
    if (this != &s)
    {
        free();
        elements = s.elements;
        first_free = s.first_free;
        cap = s.cap;
        s.elements = s.first_free = s.cap = nullptr;
    }
    return *this;
}

template <typename T>
Vec<T>::~Vec()
{
    free();
}

template <typename T>
void Vec<T>::alloc_n_move(size_t new_cap)
{
    auto newdata = alloc.allocate(new_cap);
    auto dest = newdata;
    auto elem = elements;
    for (size_t i = 0; i != size(); ++i)
        alloc.construct(dest++, std::move(*elem++));
    free();
    elements = newdata;
    first_free = dest;
    cap = elements + new_cap;
}
template <typename T>
void Vec<T>::reallocate()
{
    auto newcapacity = size() ? 2 * size() : 1;
    alloc_n_move(newcapacity);
}
template <typename T>
void Vec<T>::reserve(size_t new_cap)
{
    if (new_cap <= capacity())
        return;
    alloc_n_move(new_cap);
}

// resize
// 重设容器大小以容纳 count 个元素。
// 若当前大小大于 count ,则减小容器为其首 count 个元素。
// 若当前大小小于 count ,则后附额外元素,并以 value 的副本初始化。
template <typename T>
void Vec<T>::resize(size_t count, const T &s)
{
    if (count > size())
    {
        if (count > capacity())
            reserve(count * 2);
        for (size_t i = size(); i != count; ++i)
            alloc.construct(first_free++, s);
    }
    else if (count < size())
    {
        while (first_free != elements + count)
            alloc.destroy(--first_free);
    }
}

template <typename T>
void Vec<T>::resize(size_t count)
{
    resize(count, T());
}

template <typename T>
void Vec<T>::push_back(const T &s)
{
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
template <typename T>
bool operator==(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return (lhs.size() == rhs.size() &&
            std::equal(lhs.begin(), lhs.end(), rhs.begin()));
}

template <typename T>
bool operator!=(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return !(lhs == rhs);
}

template <typename T>
bool operator<(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
                                        rhs.end());
}

template <typename T>
bool operator>(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return rhs < lhs;
}

template <typename T>
bool operator<=(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return !(rhs < lhs);
}

template <typename T>
bool operator>=(const Vec<T> &lhs, const Vec<T> &rhs)
{
    return !(lhs < rhs);
}
int main()
{
    Vec<std::string> vec;
    vec.reserve(6);
    std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl;

    vec.reserve(4);
    std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl;

    vec.push_back("hello");
    vec.push_back("world");

    vec.resize(4);

    std::string its = "my_name_is_yer";
    vec.emplace_back(its.begin(), its.end());
    vec.emplace_back("back_123");
    vec.emplace_back(20, 'c');

    for (auto i = vec.begin(); i != vec.end(); ++i)
        std::cout << *i << std::endl;
    std::cout << "-EOF-" << std::endl;

    vec.resize(1);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        std::cout << *i << std::endl;
    std::cout << "-EOF-" << std::endl;

    Vec<std::string> vec_list{"hello", "world", "pezy"};

    for (auto i = vec_list.begin(); i != vec_list.end(); ++i)
        std::cout << *i << " ";
    std::cout << std::endl;

    // Test operator==

    const Vec<std::string> const_vec_list{"hello", "world", "pezy"};
    if (vec_list == const_vec_list)
        for (const auto &str : const_vec_list)
            std::cout << str << " ";
    std::cout << std::endl;

    // Test operator<
    const Vec<std::string> const_vec_list_small{"hello", "pezy", "ok"};
    std::cout << (const_vec_list_small < const_vec_list) << std::endl;

    // Test []
    std::cout << const_vec_list_small[1] << std::endl;
    while (1)
        ;
}

16.59

// 假定 s 是一个 string,解释调用 svec.emplace_back(s)会发生什么
1.转发扩展包
2. s 传递是值传递,也就是会调用string newstring(s),拷贝构造函数

16.60

// 解释 make_shared 是如何工作的。
最简单的一种重载是
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

我们在使用的时候, shared_ptr<string> a=make_shared<string>("123");

1. 转发扩展包给 class T 的构造函数
2. 内部提取地址到shared<T> 
    

16.61

// 解释 make_shared 是如何工作的。
// 最简单的一种重载是
// template< class T, class... Args >
// shared_ptr<T> make_shared( Args&&... args );

// 我们在使用的时候, shared_ptr<string> a=make_shared<string>("123");

// 1. 转发扩展包给 class T 的构造函数
// 2. 内部提取地址到shared<T>

#include <memory>
#include <string>
#include <iostream>
using namespace std;

template <class T, class... Args>
shared_ptr<T> my_make_shared(Args &&... args)
{
    shared_ptr<T> ret(new T(std::forward<Args>(args)...));

    return ret;
}

int main(int argc, char const *argv[])
{
    shared_ptr<string> s1 = my_make_shared<string>("123");
    std::cout << *s1 << std::endl;
    while (1)
        ;
    return 0;
}

16.62

// 16.62 定义你自己版本的 hash<Sales_data>, 并定义一个 Sales_data 对象的 unorder_multise。将多条交易记录保存到容器中,并打印其内容
// p396
// 无序容器需要提供 元素的== 以及哈希函数
// 需要提供hash<类型>的模版,一个 ()运算返回size_t 表示hash值
#include <string>
#include <vector>
#include <iostream>
#include <unordered_set>
using namespace std;

class Sales_data
{
    friend bool operator==(const Sales_data &, const Sales_data &);
    friend std::hash<Sales_data>;

private:
    string isbn;
    unsigned units_sold = 0;
    double revenue = 0.0;

public:
    Sales_data(string isbn, unsigned units_sold, double revenue) : isbn(isbn), units_sold(units_sold), revenue(revenue) {}

    double avg_price() { return revenue / units_sold; }
    void print() { std::cout << "name=" << isbn << " aver_price= " << avg_price() << std::endl; }
    string prints() { return string("name=") + isbn + " aver_price= " + to_string(avg_price()); }
};

bool operator==(const Sales_data &a, const Sales_data &b)
{
    return (a.isbn == b.isbn) && (a.revenue == b.revenue) && (a.units_sold == b.units_sold);
}

namespace std
{
template <>
struct hash<Sales_data>
{
    typedef size_t result_type;
    typedef Sales_data argument_type;
    size_t operator()(const Sales_data &s) const
    {
        return hash<string>()(s.isbn) ^
               hash<unsigned>()(s.units_sold) ^
               hash<double>()(s.revenue);
    }
};
} // namespace std

int main(int argc, const char **argv)
{
    Sales_data A1("A1", 5, 17);
    A1.print();

    std::unordered_multiset<Sales_data> mset;

    mset.emplace(A1);
    mset.emplace("C++ Primer", 5, 9.99);

    for (auto ch : mset)
        std::cout << "hash()=" << std::hex << std::hash<Sales_data>()(ch) << ch.prints() << std::endl;

    while (1)
        ;
    return 0;
}

16.63

16.64


// 16.64 为上一题的模版编写特例化版本来处理vector<const char*>。编写程序使用这个特例化版本。
// 16.63 定义一个函数模版,统计一个给定值在一个vecor中出现的次数。
// 测试你的函数,分别传递给它一个double的vector,一个int的vector以及一个string的vector。

#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
int GetCount(T elem, const vector<T> &v)
{
    std::cout << __LINE__ << "   :GetCount(T elem, const vector<T> &v)" << std::endl;
    int i = 0;
    for (auto ch : v)
    {
        if (ch == elem)
            i++;
    }
    return i;
}

template <>
int GetCount(const char *elem, const vector<const char *> &v)
{
    std::cout << __LINE__ << "   :GetCount(const char *elem, const vector<const char *> &v)" << std::endl;
    int i = 0;
    for (auto ch : v)
    {
        if (string(ch) == string(elem))
            i++;
    }
    return i;
}

int GetCount(const char *elem, const vector<string> &v)
{
    std::cout << __LINE__ << "    :GetCount(const char *elem, const vector<string> &v)" << std::endl;
    return GetCount(string(elem), v);
}

int GetCount(const int elem, const vector<double> &v)
{
    return GetCount((double)elem, v);
}

int main(int argc, char const *argv[])
{
    vector<string> s({"123", "456", "789", "123", "222"});
    std::cout << GetCount("123", s) << std::endl;
    std::cout << GetCount(string("123"), s) << std::endl;

    vector<int> i = {1, 2, 3, 4, 5, 6, 1, 2};
    std::cout << GetCount(1, i) << std::endl;

    // vector<double> d = {1.0, 2.5, 3, 4, 5, 6, 1, 2};
    // std::cout << GetCount(1, d) << std::endl;

    vector<const char *> s3({"123", "456", "789", "123", "222"});
    std::cout << GetCount("123", s3) << std::endl;

    while (1)
        ;
    return 0;
}

16.65

// 在16.3节中我们定义了两个重载的 debug_rep 版本,一个接受 const char* 参数,另一个接受 char * 参数。将这两个函数重写为特例化版本。
// 16.48 编写你自己版本的 debug_rep 函数。

#include <iostream>
#include <string>
#include <sstream>
using std::string;

string debug_rep(const string &t)
{
    return "String:" + t;
}

template <typename T>
string debug_rep(const T &t)
{
    std::ostringstream ret;
    ret << t;
    return ret.str();
}

template <typename T>
string debug_rep(T *t)
{
    std::ostringstream ret;
    ret << "Point Addr=" << t;
    if (t == nullptr)
        ret << "nullptr";
    else
        ret << debug_rep(*t);
    return ret.str();
}
template <>
string debug_rep(const char *t)
{
    return string("const char*") + debug_rep(string(t));
}
template <>
string debug_rep(char *t)
{
    return "char*" + debug_rep(string(t));
}

// string debug_rep(const char *t)
// {
//     return "const char*" + debug_rep(string(t));
// }

int main(int argc, const char **argv)
{

    std::cout << debug_rep("123456") << std::endl;
    while (1)
        ;

    return 0;
}

16.66

重载debug_rep 函数与特例化它相比,有何优点和缺点?

特例化是的本质是实例化一个模版,不影响函数匹配.  而重载如果有非模版的,直接使用该版本
重载会改变函数匹配顺序,因为增加了新函数
几个函数都提供同样好的匹配的情况下,编译器会选择非板版本

16.67

定义特例化版本会影响 debug_rep 的函数匹配吗?如果不影响,为什么?

不会改变,特例化模板函数不会重载函数,不会影响函数匹配顺序 也就是不产生新的函数类型
> p262

特例化是的本质是实例化一个模版,不影响函数匹配.  而重载如果有非模版的,直接使用该版本

也就是他的匹配顺序是模版的顺序,模版之后下面来找具体的特例化(**可以理解为先匹配模版后才能匹配特例化**)

而一个重载和一个模版是平等关系的
​```cpp
重载的版本(包括模版)-------------模版
                               |
                        |~~~~~~~~~~~~~~~|
                    特例化              通用的模版实现

原文地址:https://www.cnblogs.com/zongzi10010/p/12763083.html