Primer C++笔记记录(第十六章)

Primer C++笔记记录(第十六章)

本书为Primer C++ 中文第五版

练习题笔记

在这里插入图片描述

(16.1)
当调用函数模板时,编译器通常用函数实参来为推断模板实参。
编译器用推断出的模板参数来实例化一个特定版本的函数。
当编译器实例化一个模板时,它使用实际的模板实参代替对应的模板参数来创建出模板的一个“实例”。
(16.2)
#include <functional>//less<>的头文件
template <typename T>
int compare(const T &v1, const T &v2) {
    if(std::less<T>()(v1, v2)) return -1;
    if(std::less<T>()(v2, v1)) return 1;
    return 0;
}
(16.3)
/**** test3 ****/
class Sales_data {
friend bool operator<(const Sales_data&, const Sales_data&);
public:
    Sales_data(const string& s, double p): isbn(s), price(p) { }
    string isBn() const { return isbn; }
private:
    string isbn;
    double price;
};

bool operator<(const Sales_data& sd1, const Sales_data& sd2) {
    return sd1.isBn() < sd2.isBn();
}

void test3() {
    string s1("t1"), s2("t2");
    Sales_data sd1(s1, 4.56), sd2(s2, 8.92);
    print(compare(sd1, sd2));
    print(compare(s1, s2));
}
/*
需要重载operator<,否则会出现如下错误
error: no match for ‘operator<’ (operand types are ‘const Sales_data’ and ‘const Sales_data’)
*/
(16.4)
/**** test4 ****/
template <typename I, typename V>
I findV(I beg, I end, V value) {
    while(beg != end) {
        if(*beg == value) break;
        ++beg;
    }
    return beg;
}

void test4() {
    vector<int> v = {1, 2, 3, 4, 5};
    auto f = findV(v.begin(), v.end(), 3);
    cout << (f == v.end() ? "not found\n": "found\n");
    list<string> l = {"a", "b", "c", "d", "e"};
    auto fs = findV(l.begin(), l.end(), "sstr");
    cout << (fs == l.end() ? "not found\n": "found\n");
}
(16.5)
/**** test5 ****/
template<typename I>
void printArr(I const& i) {
    for(auto const& e : i) {
        cout << e << " ";
    }
    cout << endl;
}

void test5() {
    char c[] = {'a', 'b', 'c'};
    int i[] = {1, 2, 3};
    printArr(c);
    printArr(i);
}
(16.6)
begin获取一个数组的引用,指向数组中的第一个元素
end获取数组的引用和大小,指向这个引用加上数组的大小
/**** test6 ****/
/**
 *T(&arr)[s]
 *参数声明一个名为arr的变量,该变量是对一个数组类型T的引用
 *将数组传递给函数而不使其衰退为指针的方法
 *可以在函数中使用数组,就像在声明它的作用域中使用数组样
 */
template<typename T, unsigned s>
T* beginOf(T(&arr)[s]) {
    return arr;
}
template<typename T, unsigned s>
T* endOf(T(&arr)[s]) {
    return arr+s;
}

void test6() {
    string s[] = {"abc", "def", "ghi"};
    cout << *(beginOf(s)) << endl;
    cout << *(endOf(s) - 1) << endl;
}
(16.7)
/**** test7 ****/
template<typename T, unsigned s>
constexpr unsigned getSize(T(&arr)[s]) {
    return s;
}

void test7() {
    int i[] = {1, 2, 3};
    cout << getSize(i) << endl;
}
(16.8)
因为大多数类通常会定义"!="而不是"<",使用"!="会减少错误

在这里插入图片描述

(16.9)
类模板:模板定义,可以从它实例化出特定的类。类模板的定义以关键字template开始,
后面跟尖括号对<>,其内为一个用逗号分隔的一个或多个模板参数的列表,随后是类的定义
函数模板:模板定义,可从它实例化出特定函数。函数模板的定义以关键字template开始,
后面跟尖括号对<>,其内为一个用逗号分隔的一个或多个模板参数的列表,随后是函数的定义
(16.10)
当一个类模板实例化时,编译器会使用模板实参来实例化出特定的类,它会重写类模板,
将模板参数的每个实例替换为给定的模板实参,每个实例都形成一个独立的类。
(16.11)
template <typename elemType> class ListItem;
template <typename elemType> class List {
    List();
    List(const List&);
    List& operator=(const List&);
    ~List();
    void insert(ListItem<elemType> *ptr, elemType value);
private:
    ListItem<elemType> *front, *end;
};
(16.12)
#include <iostream>
#include <vector>
#include <string>
#include <memory>

using namespace std;

template <typename T> class BlobPtr;
template <typename T>
bool operator==(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
template <typename T>
bool operator< (const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);


template <typename T>
class Blob {
    friend class BlobPtr<T>;
public:
    typedef T value_type;
    typedef typename vector<T>::size_type size_type;
    Blob(): data(make_shared<vector<T>>()) { }
    Blob(initializer_list<T> il): data(make_shared<vector<T>>(il)) { }
    size_type size() const { return data->size(); }
    void push_back(const T& t) { data->push_back(t); }
    void push_back(T &&t) { data->push_back(move(t)); }
    void pop_back() {
        check(0, "pop_back on empty Blob");
        data->pop_back();
    }
    T& back() {
        check(0, "back on empty Blob");
        return data->back();
    }
    const T& back() const {
        check(0, "back on empty Blob");
        return data->back();
    }
    T& operator[](size_type i) {
        check(i, "subscript out of range");
        return (*data)[i];
    }
    const T& operator[](size_type i) const {
        check(i, "subscript out of range");
        return (*data)[i];
    }
private:
    shared_ptr<vector<T>> data;
    void check(size_type i, const string& msg) const;
};

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


template <typename T>
class BlobPtr {
    friend bool operator==<T>(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
    friend bool operator< <T>(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs);
public:
    BlobPtr(): curr(0) { }
    BlobPtr(Blob<T>& a, size_t sz = 0): wptr(a.data), curr(sz) { }
    T& operator*() const {
        auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    BlobPtr& operator++() {
        check(curr, "increment past end of StrBlob");
        ++curr;
        return *this;
    }
    BlobPtr& operator--() {
        --curr;
        check(curr, "decrement past begin of StrBlob");
        return *this;
    }
    BlobPtr operator++(int) {
        BlobPtr ret = *this;
        ++*this;
        return ret;
    }
    BlobPtr operator--(int) {
        BlobPtr ret = *this;
        --*this;
        return ret;
    }
private:
    shared_ptr<vector<T>> check(size_t, const string&) const;
    weak_ptr<vector<T>> wptr;
    size_t curr;
};
template <typename T>
bool operator==(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs) {
    if(lhs.wptr.lock() != rhs.wptr.lock()) {
        throw runtime_error("ptrs to different Blobs!");
    }
    return lhs.curr == rhs.curr;
}
template <typename T>
bool operator<(const BlobPtr<T>& lhs, const BlobPtr<T>& rhs) {
    if(lhs.wptr.lock() != rhs.wptr.lock()) {
        throw runtime_error("ptrs to different Blobs!");
    }
    return lhs.curr < rhs.curr;
}
template <typename T>
shared_ptr<vector<T>> BlobPtr<T>::check(size_t i, const string& msg) const {
    auto ret = wptr.lock();
    if(!ret)
        throw runtime_error("undound BlobPtr"); 
    if(i >= ret->size())
        throw out_of_range(msg);
    return ret;
}

int main() {
    Blob<int> ia = {0,1,2,3,4};
    cout << ia[2] << ia.back() << endl;
    BlobPtr<int> bp(ia);
    cout << *bp << endl;
    cout << (bp == ++bp) << endl;
    cout << *bp << endl;
    bp--;bp--;
    return 0;
}
(16.13)
添加了相等运算符和小于运算符
(16.14)
#include <iostream>
#include <string>

using namespace std;

template <unsigned H, unsigned W>
class Screen {
public:
    typedef string::size_type pos;
    Screen() = default;
    Screen(char c): contents(H*W, c) { }
    char get() const { return contents[cursor]; }
    Screen &move(pos r, pos c) {
        pos row = r * width;
        cursor = row + c;
        return *this;
    }
    void set(pos r, pos c, char a) {
        contents[r*width + c] = a;
    }
    friend ostream& operator<<(ostream& os, const Screen<H, W>& c) {
        unsigned int i, j;
        for(i = 0; i < c.height; ++i) {
            os << c.contents.substr(i*W, W) << endl;
        }
        return os;
    }
    friend istream& operator>>(istream& is, Screen<H, W>& c) {
        char a;
        is >> a;
        string tmp(H*W, a);
        c.contents = tmp;
        return is;
    }
private:
    pos cursor = 0;
    pos height = H, width = W;
    string contents;
};

int main() {
    Screen<10, 20> s;
    cin >> s;
    cout << s;
    s.move(3, 5);
    s.set(3, 5, 'z');
    cout << s.get() << endl;
    cout << s;
    return 0;
}
(16.15)
添加了输入和输出友元函数
(16.16)
#include <iostream>
#include <string>
#include <memory>

using namespace std;

template <typename T>
class Vec {
public:
    Vec() : elements(NULL),first_free(NULL), cap(NULL) { }
    Vec(initializer_list<T> l) {
        T* const newData = alloc.allocate(l.size());
        T* p = newData;
        for(const auto &t : l)
            alloc.construct(p++, t);
        elements = newData;
        first_free = cap = elements+l.size();
    }
    Vec(const Vec&);
    Vec &operator=(const Vec&);
    ~Vec();
    void push_back(const T&);
    void push_back(T &&s) {
        chk_n_alloc();
        alloc.construct(first_free++, move(s));
    }
    size_t size() const {return first_free - elements;}
    size_t capacity() const {return cap - elements;}
    T* begin() const {return elements;}
    T* end() const {return first_free;}
    //移动构造函数
    Vec(Vec &&s) noexcept : elements(s.elements), first_free(s.first_free), cap(s.cap) {
        s.elements = s.first_free = s.cap = NULL;
    }
    Vec& operator=(Vec &&s) noexcept {
        if(this != &s) {
            free();
            elements = s.elements;
            first_free = s.first_free;
            cap = s.cap;
            s.elements = s.first_free = s.cap = NULL;
        }
        return *this;
    }
private:
    static allocator<T> alloc;
    void chk_n_alloc() {
        if(size() == capacity()) reallocate();
    }
    pair<T*, T*> alloc_n_copy(const T*, const T*);
    void free();
    void reallocate();
    T* elements; //数组首元素
    T* first_free; //数组第一个空闲元素
    T* cap; //数组尾后位置
};

template <typename T>
allocator<T> Vec<T>::alloc;

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

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

template <typename T>
void Vec<T>::free() {
    if(elements) {
        for(auto p = first_free; p != elements;) {
            alloc.destroy(--p);
        }
        alloc.deallocate(elements, cap - elements);
    }
}

template <typename T>
Vec<T>::Vec(const Vec &s) {
    auto newdata = alloc_n_copy(s.begin(), s.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}

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

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

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

int main() {
    Vec<int> v = { 1, 2, 3, 4, 5 };
    Vec<int> v2;
    v2 = v;
    std::cout << v2.capacity() << "\n";
    v2.push_back(99);
    for (auto t : v2)
        std::cout << t << " ";
    std::cout << v2.capacity() << "\n";
    return 0;
}

在这里插入图片描述

(16.17)
在模板参数列表中,typenameclass两个关键字的含义相同,都是表明模板参数是一个类型。
但是当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class
(16.18)
a>
模板参数列表每个参数都需要加上typenameclass
template <typename T, typename U, typename V> void f1(T, U, V);
b>
函数内的形参名称和模板参数列表名称同名,不允许重声明模板参数
template <typename T> T f2(T&);
c>
函数在定义的时候才能指定为inline
template <typename T> T foo(T, unsigned int*);
d>
函数没有返回类型
template <typename T> void f4(T, T);
e>
合法,但是模板参数内部的Ctype是模板参数的类型,而非char
(16.19)
(16.20)
/***** test19And20 ****/
template<typename T>
void printCon19(T& t) {
    for(typename T::size_type scan = 0; scan < t.size(); ++scan) {
        cout << t[scan] << " ";
    }
    cout << endl;
}

template<typename T>
void printCon20(T& t) {
    for(auto scan = t.begin(); scan < t.end(); ++scan) {
        cout << *scan << " ";
    }
    cout << endl;
}

void test19And20() {
    vector<int> vi = {1, 2, 3, 4};
    deque<string> ds = {"hello", "world", "hahaha"};
    printCon19(vi);
    printCon19(ds);
    printCon20(vi);
    printCon20(ds);
}

在这里插入图片描述

(16.21)
#include <iostream>
#include <string>

using namespace std;

class DebugDelete {
public:
    DebugDelete(ostream &s = cerr): os(s) { }
    template <typename T> void operator()(T *p) const {
        os << "deleting unique_ptr\n";
        delete p;
    }
private:
    ostream& os;
};


int main() {
    double* p = new double;
    DebugDelete d;
    d(p);
    int* ip = new int;
    DebugDelete()(ip);
    return 0;
}
(16.22)
用法大致如下,就不再全部改写了。
unique_ptr<int, DebugDelete> p(new int, DebugDelete());
unique_ptr<string, DebugDelete> sp(new string, DebugDelete());
(16.23)
shared_ptr计数减少到0的时候,会调用DebugDelete()函数删除指针
(16.24)
template <typename T, typename It>
Blob<T>::Blob(It b, It e): data(make_shared<vector<T>>(b, e)) { }

在这里插入图片描述

(16.25)
extern template class vector<string>
实例化声明,当编译器遇到extern模板声明时,它不会在本文件中生成实例化代码。
并且表示承诺在程序的其他位置有该实例化的一个非extern声明(定义)
template class vector<Sales_data>
Sales_data实例化vector
(16.26)
不可以;因为在一个类模板的实例化定义中,所用的类型必须能用于模板的所有成员函数。
(16.27)
a>
char 实例化模板
b>
double 实例化模板
c>
int 实例化模板
d>
存在该实例的定义,不需要再次实例化
e>
存在该实例的定义,不需要再次实例化
f>
string 实例化模板

在这里插入图片描述

(16.28)
#include <iostream>
#include <string>
#include <memory>

using namespace std;

class DebugDelete {
public:
    DebugDelete(ostream &s = cerr): os(s) { }
    template <typename T> void operator()(T *p) const {
        os << "deleting unique_ptr: " << *p << "\n";
        delete p;
    }
private:
    ostream& os;
};

template <typename T>
class shared_ptr_tmp {
public:
    shared_ptr_tmp(): spt(nullptr), use_count(new size_t(1)), deleter(DebugDelete()) { }
    shared_ptr_tmp(T* tmp): spt(tmp), use_count(new size_t(1)), deleter(nullptr) { }
    shared_ptr_tmp(T* tmp, function<void(T*)> d) {
        spt = tmp;
        deleter = d;
        use_count = new size_t(1);
    }
    //copy
    shared_ptr_tmp(const shared_ptr_tmp& tmp): spt(tmp.spt), use_count(tmp.use_count), deleter(tmp.deleter) {
        ++*use_count;
    }
    //Copy assignment
    shared_ptr_tmp& operator=(const shared_ptr_tmp& tmp) {
        ++*(tmp.use_count);
        ~shared_ptr_tmp();
        spt = tmp.spt;
        use_count = tmp.use_count;
        deleter = tmp.deleter;
    }
    T& operator*() { return *spt; }

    void reset(T *pointer, std::function<void(T*)> d) {
        if (spt != pointer) {
            ~shared_ptr_tmp();
            spt = pointer;
            use_count = new size_t(1);
        }
        deleter = d;
    }

    ~shared_ptr_tmp() {
        if(--*use_count == 0 && spt) {
            deleter? deleter(spt) : delete spt;
            delete use_count;
        }else if(!spt){
            delete use_count;
        }
        use_count = nullptr;
        spt = nullptr; 
    }
private:
    T* spt;
    function<void(T*)> deleter;
    size_t* use_count;
};

template <typename T, typename D>
class unique_ptr_tmp {
public:
    unique_ptr_tmp() = default;
    unique_ptr_tmp(T* up): upt(up) { }
    T& operator*() const { return *upt; }
    T* get() const noexcept { return upt; }
    void reset(T* p) noexcept { deleter(upt); upt = p;}
    ~unique_ptr_tmp()
    {
        deleter(upt);
    }
private:
    T* upt = nullptr;
    D  deleter = D();
};

int main() {
    shared_ptr_tmp<int> i(new int(42));
    cout << *i << endl;
    shared_ptr_tmp<string> s(new string("hello"), DebugDelete());
    cout << *s << endl;
    unique_ptr_tmp<int, DebugDelete> ui(new int(33));
    return 0;
}
(16.29)

(16.30)

(16.31)

在这里插入图片描述

(16.32)
编译器使用函数调用中的实参类型来寻找模板实参,用这些模板实参生成的函数版本与给定的函数调用最为匹配
(16.33)
const转换:可以将一个非const对象的引用(或指针)传递给一个const的引用(或指针)形参
数组或函数指针转换:如果函数形参不是引用类型,则可以对数组或函数类型的实参应用正常的指针转换;
一个数组实参可以转换成一个指向其首元素的指针;一个函数实参可以转换成一个该函数类型的指针;
(16.34)
乍一看,以为是一样的。但实则不同
a> char[3]  char[6], 不合法,会报错
b> char[4]  char[4], T的类型为char*
(16.35)
a> 合法,T的类型为char
b> 合法,T的类型为double,因为float可以转换成int
c> 合法,T的类型为char
d> 不合法
(16.36)
a> T的类型为int*
b> T1T2的类型都为int*
c> T的类型为const int*
d> T1T2的类型都为const int*
e> 不合法,类型不一致
f> T1类型为int*,T2类型为const int*

在这里插入图片描述

(16.37)
void test37() {
    int a = 2;
    double b = 2.3;
    cout << max<double>(a, b) << endl;
}
(16.38)
make_share如果不提供显示模板实参,则无法给参数分配适当的内存
(16.39)
void test39() {
    cout << compare<string>("abc", "aaaaaa") << endl;
}

在这里插入图片描述

(16.40)
函数合法,但是对于传递的实参类型有限制,实参类型必须支持operator+(int)操作。
/**** test 40 ****/
template <typename It>
auto fcn3(It beg, It end) -> decltype(*beg + 0) {
    return *beg;
}

void test40() {
    vector<int> vi = {0, 1, 2, 3};
    auto target = fcn3(vi.begin(), vi.end());
    cout << target << endl;
    //非法
    /*
    vector<string> vs = {"z", "l", "f"};
    auto tar = fcn3(vs.begin(), vs.end());
    cout << tar << endl;
    */
    vector<char> vc = {'z', 'l', 'f'};
    auto tarc = fcn3(vc.begin(), vc.end());
    cout << tarc << endl;
}

(16.41)
/**** test 41 ****/
template <typename TL, typename TR>
auto sum(TL l, TR r) -> decltype(l + r) {
    return l + r;
}

void test41() {
    cout << sum(12345678987654321, 12345678987654321) << endl;
}

在这里插入图片描述

(16.42)
a> T的类型为int&, val则是int& &&折叠成int&
b> T的类型为const int&, val则是const int& &&折叠成const int&
c> T的类型为int, val则是int&&
(16.43)
因为 i = ci 是一个赋值语句,返回一个左值。所以和上面的a一样,Tint&, valint&
(16.44)
T:
a>T: int
b>T: int, const会被忽略
c>T: int, i*ci返回一个右值将会被复制给T
const T&:
a>T: int
b>T: int
c>T: int
(16.45)
对于42,是一个右值,所以T的类型是int&&,而val类型是int&& &&将会被折叠为int&&,所以vector<int&&>合法。
对于int,是一个左值。所以T的类型是int&,而val类型是int& &&将被折叠为int&,所以vector<int&>非法。

在这里插入图片描述

(16.46)
在每次迭代中,解引用操作符*返回一个左值,然后这个左值通过move操作变成右值;
因为成员函数构造函数使用右值引用而不是左值引用。

在这里插入图片描述

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

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

void test47() {
    int i = 1, j = 2;
    flip(g, j, 1);
    cout << i << " " << j << endl;
}

在这里插入图片描述

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

template <typename T> string debug_rep(T *p) {
    ostringstream ret;
    ret << "pointer: " << p;
    if(p)
        ret << " " << debug_rep(*p);
    else
        ret << " null pointer" ;
    return ret.str();
}

void test48() {
    string s("hi");
    cout << debug_rep(s) << endl;
    cout << debug_rep(&s) << endl;
}
(16.49)
g(T t)
g(T *t)
g(T t)
g(T *t)
*****
f(T t)
f(T t)
f(T t)
f(const T *t)
(16.50)
/**** test 49&50 ****/
template <typename T> void f(T t) {
    cout << "f(T t)" << endl;
}
template <typename T> void f(const T *t) {
    cout << "f(const T *t)" << endl;
}
template <typename T> void g(T t) {
    cout << "g(T t)" << endl;
}
template <typename T> void g(T *t) {
    cout << "g(T *t)" << endl;
}

void test49() {
    int i = 42, *p = &i;
    const int ci = 0, *p2 = &ci;
    g(42);g(p);g(ci);g(p2);
    cout << "*****" << endl;
    f(42);f(p);f(ci);f(p2);
}

在这里插入图片描述

(16.51)
3 3
2 2
1 1
0 0
(16.52)
/**** test 51&52 ****/
template<typename T, typename... Args>
void foo(const T &t, const Args& ... args) {
    cout << sizeof...(Args) << " " << sizeof...(args) << endl;
}

void test51() {
    int i = 0;
    double d = 3.14;
    string s = "how now brown cow";
    foo(i, s, 42, d);
    foo(s, 42, "hi");
    foo(d, s);
    foo("hi");
}

在这里插入图片描述

(16.53)
/**** test 53&54&55 ****/
template <typename T> ostream &printt(ostream &os, const T &t) {
    return os << t << "\n";
}
template <typename T, typename... Args>
ostream &printt(ostream &os, const T &t, const Args&... args) {
    os << t << ", ";
    return printt(os, args...);
}

void test53() {
    int i = 0;
    double d = 3.14;
    string s = "how now brown cow";
    const char* c = "hello";
    cout << "1:\n";
    printt(cout, i);
    cout << "2:\n";
    printt(cout, i, d);
    cout << "5:\n";
    printt(cout, i, d, s, c);
}
(16.54)
编译失败
(16.55)
编译报错,error: no matching function for call to printt(std::ostream&)

在这里插入图片描述

(16.56)
/**** test 56 ****/
template <typename T> string debug_repp(const T &t) {
    ostringstream ret;
    ret << t;
    return ret.str();
}

template <typename... Args>
ostream &errorMsg(ostream &os, const Args&... args) {
    return printt(os, debug_repp(args)...);
}

void test56() {
    errorMsg(cerr, "other", 1, 3.15);
}
(16.57)
可变参数版本的errorMsg参数不仅可以有不同的参数个数,也可以有不同的参数类型。

在这里插入图片描述

(16.58)
template<typename... Args> void emplace_back(Args... args) {
    chk_n_alloc();
    alloc.construct(first_free++, forward<Args>(args)...);
}
(16.59)
construct调用中的扩展为forward<string>(s),将得到一个左值参数,然后将这个参数传递给string的构造函数创建新元素。
(16.60)
make_shared应该是一个可变参数模板函数,它将所有的参数转发给底层构造函数,
这些构造函数在动态内存中分配和初始化一个对象。然后通过包装原始指针构建一个shared_ptr
(16.61)
/**** test 61 ****/
template <typename T, typename... Args>
auto make_share(Args&&... args) -> shared_ptr<T> {
    return shared_ptr<T>(new T(forward<Args>(args)...));
}

void test61() {
    auto n = make_share<int>(42);
    cout << *n << endl;
    auto s = make_share<string>(10, 'c');
    cout << *s << endl;
}

在这里插入图片描述

(16.62)
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.bookNo) ^
                   hash<unsigned>()(s.units_sold) ^
                   hash<double>()(s.revenue);
        }
    };
}

unordered_multiset<Sales_data> SDset;
SDset.emplace("TLBB", 5, 6.66);
(16.63)
(16.64)
/**** test 63&64 ****/
template <typename T> size_t count(vector<T> const& v, T value) {
    size_t counts = 0;
    for(auto t : v)
        if(t == value) ++counts;
    return counts;
}

template <> size_t count(vector<const char*> const& v, const char* value) {
    size_t counts = 0;
    for(auto t : v)
        if(t == value) ++counts;
    return counts;
}

void test63() {
    vector<int> vi = {0, 1, 2, 2, 2, 3, 4};
    vector<double> vd = {0.1, 0.2, 0.2, 0.3};
    vector<string> vs = {"a", "ab", "ab", "abc"};
    string s = "ab";
    cout << count(vi, 2) << " " << count(vd, 0.2) << " " << count(vs, s) << endl;
    vector<const char*> vcc = {"a", "ab", "ab", "abc"};
    cout << count(vcc, "ab") << endl;
}
(16.65)
/**** test 65 ****/
template<>
string debug_rep(const char* s) {
    ostringstream ret;
    cout << "<>const char*: \n";
    ret << s;
    return ret.str();
}

template<>
string debug_rep(char* s) {
    ostringstream ret;
    cout <<"<>char*: \n";
    ret << s;
    return ret.str();
}

void test65() {
    char p[] = "abcd";
    cout << debug_rep(p) << endl;
    const char* pp = "zxcv";
    cout << debug_rep(pp) << endl;
}
(16.66)
重载会改变函数匹配,但是特例化并不会
(16.67)
不影响,定义函数模板的特例化版本时,本质上是接管了编译器的工作。
所以特例化版本本质是是一个实例,而不是函数名的一个重载版本,不影响函数匹配。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦