16.1
编译器用推断出的模板参数为我们实例化一个特定版本的函数,当编译器实例化一个模板时,它使用实际的模板实参代替对应的模板参数来创建一个新的实例。
16.2
template<typename T>
int compare(const T&v1, const T&v2)
{
if (v1 < v2)return -1;
if (v2 < v1)return 1;
return 0;
}
16.3
error: no match for ‘operator<’
16.4
template <typename I,typename S>
I find(I beg, I end, const S& s)
{
for (; beg != end; ++beg)
{
if (*beg == s)
{
return beg;
}
}
}
16.5
#include <iostream>
#include <string>
template<typename T>
void print(T const& arr)
{
for (auto const& elem : arr)
std::cout << elem << std::endl;
}
int main()
{
std::string s[] = { "ssss", "aaa", "ssssss" };
char c[] = { 'a', 'b', 'c', 'd' };
int i[] = { 1 };
print(i);
print(c);
print(s);
return 0;
}
16.6
#include<string>
#include<iostream>
template <typename A,unsigned N>
A* Begin(A(&a)[N])
{
return a;
}
template <typename A, unsigned N>
A* End(A(&a)[N])
{
return a+N;
}
int main()
{
std::string s[] = { "abc","efd","aaa" };
std::cout << *Begin(s) << std::endl;
std::cout << *End(s) << std::endl;
}
16.7
#include<string>
#include<iostream>
template <class A,unsigned N> constexpr
int sizeofArray(A(&a)[N])
{
return N;
}
int main()
{
std::string s[] = { "aaa","bbb" };
std::cout << sizeofArray(s) << std::endl;
}
16.8
更多的类定义了 “!=” 运算符,而不是 “<” 运算符。这么做可以减少与模板类一起使用的类的要求。简而言之,为了使用模板类,类必须满足一些要求,比如实现 “==” 或 “<” 运算符等。而 “!=” 运算符在 C++ 中已经默认定义了,因此如果一个类只需要实现 “!=” 运算符,就可以作为模板类的类型参数。这样,使用该模板类的用户就不需要为了满足要求而在类中额外实现 “<” 运算符。
16.9
一个函数模板是一个公式,可以从中生成不同类型版本的该函数。
一个类模板是用来生成类的蓝图的,编译器不能为类模板推断模板参数类型,为了使用类模板,必须在模板名字的尖括号中提供额外信息。
16.10
使用类模板必须提供显示额外实参,类模板示例化一个类时,会将模板参数T的每个实例替换为给定的模板实参。
16.11
模板类不是一个类型,当使用它的时候必须给定额外实参
public:
void insert(ListItem<elemType> *ptr,elemType value);
private:
ListItem<elemtType> *front,*end;
16.12
Blob.h
#pragma once
#include<vector>
#include<list>
#include<string>
#include<memory>
template <typename T>
class Blob {
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type; //vector类型大小
//构造函数
Blob();
Blob(std::initializer_list<T> il);
//Blob中的元素数目
size_type size()const { return data->size(); }
bool empty()const { return data->empty(); }
//添加和删除元素
void push_back(const T&t) { data->push_back(t); }
//移动版本
void push_back(T&&t) { data->data->push_back(std::move(t)); }
void pop_back();
//元素访问
T &back();
T& operator[](size_type i);
const T &back()const;
const T& operator[](size_type i)const;
private:
std::shared_ptr <std::vector<T>>data;
//若data无效,抛出msg
void check(size_type i, const std::string &msg) const;
};
template <typename T>
void Blob<T>::check(size_type i, const std::string &msg)const
{
if (i >= data->size())
throw std::out_of_range(msg);
}
template <typename T>
T& Blob<T>::back()
{
check(0, "back on empty Blob");
return data->back();
}
template <typename T>
T& Blob<T>::operator[](size_type i)
{
check(i, "subscript out of range");
return (*data)[i];
}
template <typename T>
void Blob<T>::pop_back()
{
check(0, "pop_back on empty Blob");
data->pop->back();
}
template <typename T>
const T& Blob<T>::back()const {
check(0, "back on empty Blob");
return data->back();
}
template <typename T>
const T& Blob<T>::operator[](size_type i)const {
check(i, "subscript out of range");
return (*data)[i];
}
// 构造函数
template <typename T>
Blob<T>::Blob(std::initializer_list <T>il):data(std::make_shared<std::vector<T>>(il)){}
BlobPtr.h
#pragma once
#include"Blob.h"
//首先将这些声明为模板
template <typename >class BlobPtr;
template <typename >class Blob; // 运算符==中的参数需要的
template <typename T>
bool operator==(const Blob<T>&, const Blob<T>&);
template<typename T>
class BlobPtr {
//每个Blob示例将访问权限授予用相同类型实例化的BLobptr和相等运算符
friend class BlobPtr(T);
friend bool operator==<T>
(const Blob<T>&, const Blob<T>&);
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];//(*p)为本对象指向的vector
}
//递增和递减
BlobPtr<T> operator++(int);
BlobPtr<T> operator--(int) :
BlobPtr& operator++();
BlobPtr& operator--() :
private:
//若检查成功,check将返回一个指向vector的shared_ptr
std::shared_ptr<std::vector<T>> check(std::size_t, const std::string&) const;
//保存一个weak_ptr,表示底层vector可能被销毁
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr; //表示当前位置
};
//后置递增
template<class T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{
//无需检查,因为调用前置递增是会进行检查
BlobPtr ret = *this;
++*this;
return ret;
}
16.13
一对一的友好关系
16.14&15
Screen.h
#pragma once
#include <string>
#include <iostream>
template<unsigned H, unsigned W>
class Screen {
public:
typedef std::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);
friend std::ostream& operator<< (std::ostream& os, const Screen<H, W>& c)
{
unsigned int i, j;
for (i = 0; i < c.height; i++)
{
os << c.contents.substr(0, W) << std::endl;
}
return os;
}
friend std::istream& operator>> (std::istream& is, Screen& c)
{
char a;
is >> a;
std::string temp(H * W, a);
c.contents = temp;
return is;
}
private:
pos cursor = 0;
pos height = H, width = W;
std::string contents;
};
template<unsigned H, unsigned W>
inline Screen<H, W>& Screen<H, W>::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
main.cpp
#include "Screen.h"
#include <iostream>
int main()
{
Screen<5, 5> scr('c');
Screen<5, 5> scr2;
std::cout << scr;
std::cin >> scr2;
std::cout << scr2;
return 0;
}
16.16
#pragma once
#include <iostream>
#include<initializer_list>
using namespace std;
template<class T>
class Vec;
template<class T>
bool operator== (const Vec<T>& lhs, const Vec<T>& rhs);
template<class T>
bool operator!= (const Vec<T>& lhs, const Vec<T>& rhs);
template<class T>
bool operator> (const Vec<T>& lhs, const Vec<T>& rhs);
template<class T>
bool operator< (const Vec<T>& lhs, const Vec<T>& rhs);
template<typename T>
class Vec
{
friend bool operator==<T> (const Vec<T>& lhs, const Vec<T>& rhs);
friend bool operator!=<T> (const Vec<T>& lhs, const Vec<T>& rhs);
friend bool operator><T> (const Vec<T>& lhs, const Vec<T>& rhs);
template<typename T> friend bool operator< (const Vec<T>& lhs, const Vec<T>& rhs);
public:
Vec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
Vec(initializer_list<T>il)
{
auto data = alloc_n_copy(il.begin(), il.end());
elements = data.first;
first_free = cap = data.second;
}
Vec(const Vec&);
Vec(Vec&& vec)noexcept :elements(vec.elements), first_free(vec.first_free), cap(vec.cap)
{
vec.elements = vec.first_free = vec.cap = nullptr;
}
Vec& operator=(Vec&& rhs)noexcept
{
if (this != &rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
~Vec();
Vec& operator=(initializer_list<T>);
Vec& operator= (const Vec&);
T& operator[](size_t n);
const T& operator[](size_t n)const;
void push_back(const T&);
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; }
void reserve(size_t n);
void resize(size_t n, const T& s = " ");
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>
pair<T*, T*> Vec<T>::alloc_n_copy(const T* b, const T* e)
{
auto data = alloc.allocate(e - b);
return pair<T*, T*>(data, uninitialized_copy(b, e, data));
}
template<typename T>
void Vec<T>::free()
{
if (elements) {
auto b = first_free;
while (b != elements)
{
alloc.destroy(--b);
}
alloc.deallocate(elements, cap - elements);
}
}
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++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
template<typename T>
Vec<T>::Vec(const Vec& s)
{
auto data = alloc_n_copy(s.begin(), s.end());
elements = data.first;
first_free = cap = data.second;
}
template<typename T>
Vec<T>& Vec<T>::operator=(const Vec& rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
template<typename T>
void Vec<T>::push_back(const T& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
template<typename T>
Vec<T>::~Vec()
{
free();
}
template<typename T>
void Vec<T>::reserve(size_t n)
{
if (n < size())return;
auto newcapacity = n;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i < size(); i++)
{
alloc.construct(dest++, move(*elem++));
}
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
template<typename T>
void Vec<T>::resize(size_t n, const T& s)
{
if (n > size()) {
if (n > capacity()) {
reserve(n);
}
for (size_t i = size(); i < n; ++i) {
alloc.construct(first_free++, s);
}
}
else if (n < size()) {
while ((elements + n) != first_free) {
alloc.destroy(--first_free);
}
}
}
template<typename T>
T& Vec<T>::operator[](size_t n)
{
return elements[n];
}
template<typename T>
const T& Vec<T>::operator[](size_t n)const
{
return elements[n];
}
template<typename T>
Vec<T>& Vec<T>::operator=(initializer_list<T>il)
{
auto p = alloc_n_copy(il.begin(), il.end());
free();
elements = p.first;
first_free = cap = p.second;
return *this;
}
template<typename T>
bool operator== (const Vec<T>& lhs, const Vec<T>& rhs)
{
if (lhs.size() != rhs.size()) {
return false;
}
else {
auto l_iter = lhs.begin();
auto r_iter = rhs.begin();
for (l_iter, r_iter; l_iter != lhs.end(); ++l_iter, ++r_iter) {
if (*l_iter != *r_iter) {
return false;
}
}
}
return true;
}
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 lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
template<typename T>
bool operator>(const Vec<T>& lhs, const Vec<T>& rhs)
{
return lexicographical_compare(rhs.begin(), rhs.end(), lhs.begin(), lhs.end());
}
16.17
当我们希望通知编译器一个名字表示类型时,必须用关键字typename,而不是class.
16.18
(a) 非法,U前面加typename
(b) 非法,T为类型
© 非法,内敛说明符放到尖括号之后
(d) 非法,没有说明返回类型
(e) 合法,会隐藏掉外部的声明。
template <typename T, typename U, typename V> void f1(T, U, V);
template <typename T> T f2(T&);
template <typename T> inline T foo(T, unsigned int*);
template <typename T> T f4(T, T);
typedef char Ctype;
template <typename Ctype> Ctype f5(Ctype a);
16.19
#include<vector>
#include<iostream>
template <typename T>
void printVec(T&t)
{
for (auto i : t)
std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
std::vector<int> v = { 1,2,34,5,6,7,7 };
printVec(v);
return 0;
}
16.20
#include<vector>
#include<iostream>
template <typename T>
void printVec(T&t)
{
for (auto i=t.begin();i!=t.end();++i)
std::cout << *i << " ";
std::cout << std::endl;
}
int main()
{
std::vector<int> v = { 1,2,34,5,6,7,7 };
printVec(v);
return 0;
}
16.21
#pragma once
#include<iostream>
#include<memory>
class DebugDelete
{
public:
DebugDelete(std::ostream &s =std::cerr):os(s){}
template <typename T>void operator()(T*p)const
{
os << "deleting unique_ptr" << std::endl;
delete p;
}
private:
std::ostream &os;
};
16.22&23
略
16.24
template <typename T>
template <typename It>
Blob<T>::Blob(It b, It e)
{
data(std::make_shared<std::vector<T>>(b,e)){}
}
16.25
声明,不会在本文件中生成实例代码
定义,编译器将会生成代码。
16.26
不能。因为vector类在使用默认构造函数实例化时,会要求类型T必须有默认构造函数。如果类型T没有默认构造函数,实例化会导致编译错误。因此,对于没有默认构造函数的类NoDefault,我们无法使用vector进行显式实例化。
16.27
(a)(b)©(f)发生了各自类型的实例化;
(e)(d)未发生实例化,在他们之前实例化已经完成。
16.31
shared_ptr是运行时绑定删除器,而unique_ptr则是编译时绑定删除器.
unique_ptr有两个模板参数,一个是所管理的对象类型.另一个是删除器类型.因此,删除器类型是unique_ptr类型的一部分,在编译时就可知道.删除器可直接保存在unique_ptr对象中.通过这种方式,unique_ptr避免了间接调用删除器的运行时的开销.而编译时还可以将自定义的删除器,如DebugDelete编译为内联形式.
16.32
在模板实参推断过程中,编译器使用函数调用中的实参类型来寻找模板实参,用这些模板实参生成的函数版本与给定的函数调用最为匹配。
16.33
1.const转换,将非const对象的引用或指针传递给一个const的引用或指针形参。
2.数组或函数指针转换:如果函数形参不是引用类型,可以对数组或者函数类型的实参应用正常的指针变换
16.34
(a)不合法,模板类型是引用,数组不能转换为指针
(b)合法,两个数组大小是相同的
16.35
(a)合法,char
(b)合法,double
©合法,const char
(d)不合法,二义性
16.36
(a)合法,T被推断为int*
(b)合法,T1和T2都被推断为int *
©合法,T被推断为const int *
(e)非法,T被推断为int *或是const int *,都不能匹配调用
(f)合法,T1被推断为int *,T2被推断为const int *
16.37
可以,显示指定的实参
16.38
在调用make_shared时,有时不给出参数,表示进行值初始化,有时给出的参数与维护的动态对象的类型不相关,因此,编译器无法从参数推断模板类型.就需要显示指定模板实参.
16.39
compare<string>("Hello","World");
16.40
合法,需要指向的类型支持整型的加法。
16.41
template<typename T>
auto sum(T lhs, T rhs) -> decltype( lhs + rhs)
{
return lhs + rhs;
}
16.42
(a)T:int& val:int&
(b)T:const int& val:const int&
©T:int val:int&&
16.43
T:int&
16.44
声明为T:
(a)int
(b)int
©int
因为为值传递
声明为const T&:
(a)int
(b)int
©int
16.45
用字面值常量调用g会实例化,因此正确;
用int调用g会实例化,显然会生成int&类型的vector,错误。
16.46
std::move()通过上面提到的static_cast将左值引用强制转换为右值.
16.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));
}
16.48
#pragma once
#include<string>
#include<iostream>
using namespace std;
template <typename T> std::string Debug_rep(const T& t);
template <typename T> std::string Debug_rep(T* p);
string Debug_rep(const std::string& s);
string Debug_rep(char* p);
string Debug_rep(const char* p);
template<typename T>
string Debug_rep(const T& s)
{
ostringstream ret;
ret << s << endl;
return ret.str();
}
template<typename T>
string Debug_rep(T* s)
{
ostringstream ret;
cout << "pointer:" << s << endl;
if (s)
cout << Debug_rep(*p) << endl;
else
cout << "null pointer" << endl;
}
string Debug_rep(const string& s)
{
return " " + s + " ";
}
string Debug_rep(char* p)
{
return Debug_rep(string(p));
}
string Debug_rep(const char* p)
{
return Debug_rep(string(p));
}
16.49
g(42) T=int;
g§ T=int;
g(ci) T=const int;
g(p2) T=const int;
f(42) T=int;
f§ T=int*;
f(ci) T=const int;
f(p2) T=const int;
16.50
#include <iostream>
#include <memory>
#include <sstream>
template <typename T> void f(T)
{
std::cout << "f(T)\n";
}
template <typename T> void f(const T*)
{
std::cout << "f(const T*)\n";
}
template <typename T> void g(T)
{
std::cout << "template <typename T> void g(T)\n";
}
template <typename T> void g(T*)
{
std::cout << "template <typename T> void g(T*)\n";
}
int main()
{
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
//g(42); //template <typename T> void g(T ); --is called
//g(p); //template <typename T> void g(T*); --is called
//g(ci); //template <typename T> void g(T) --is called
//g(p2); //template <typename T> void g(T*) --is called
//f(42); //f(T)
//f(p); //f(T)
//f(ci); //f(T)
f(p2); //f(const T*)
}
16.51&52
#include <iostream>
template<typename T, typename ...Args>
void foo(T t, Args ...args)
{
std::cout << sizeof...(Args) << std::endl;
std::cout << sizeof...(args) << std::endl;
}
int main()
{
int i = 0;
double d = 3.14;
std::string s = "how";
foo(i, s, 42, d); // sizeof...(Args)=3 sizeof...(args)=3
foo(s, 42, "hi"); // sizeof...(Args)=2 sizeof...(args)=2
foo(d, s); // sizeof...(Args)=1 sizeof...(args)=1
foo("hi"); // sizeof...(Args)=0 sizeof...(args)=0
foo(i, s, s, d); // sizeof...(Args)=3 sizeof...(args)=3
}
16.53
#include <iostream>
#include <string>
using namespace std;
template<typename T>
ostream &print(ostream &os, const T&t) {
return os << t;
}
template <typename T, typename ...Args>
ostream &print(ostream &os,const T&t,const Args&...rest)
{
os << t << " ";
return print(os, rest...);
}
int main()
{
int a = 10;
double b = 10.11;
string c = "abecd";
print(cout,a, b, c);
return 0;
}
16.54
代码 说明 文件 行 禁止显示状态
C2679 二进制“<<”: 没有找到接受“const T”类型的右操作数的运算符(或没有可接受的转换)
16.55
“print”: 未找到匹配的重载函数和“std::ostream &print(std::ostream &,const T &,const Args…)”: 应输入 3 个参数,却提供了 1 个
16.56
debug_rep.h
#include <iostream>
#include <memory>
#include <sstream>
template <typename T> std::string debug_rep(const T& t);
template <typename T> std::string debug_rep(T* p);
std::string debug_rep(const std::string &s);
std::string debug_rep(char* p);
std::string debug_rep(const char *p);
template<typename T> std::string debug_rep(const T& t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}
template<typename T> std::string debug_rep(T* p)
{
std::ostringstream ret;
ret << "pointer: " << p;
if (p)
ret << " " << debug_rep(*p);
else
ret << " null pointer";
return ret.str();
}
std::string debug_rep(const std::string &s)
{
return '"' + s + '"';
}
std::string debug_rep(char *p)
{
return debug_rep(std::string(p));
}
std::string debug_rep(const char *p)
{
return debug_rep(std::string(p));
}
main.cpp
#include <iostream>
#include <string>
#include<vector>
#include"debug_rep.h"
using namespace std;
template<typename T>
ostream &print(ostream &os, const T&t) {
return os << t;
}
template <typename T, typename ...Args>
ostream &print(ostream &os,const T&t,const Args&...rest)
{
os << t << " ";
return print(os, rest...);
}
template<typename T>
ostream &print(ostream &os, const T&t);
template<typename ...Args>
ostream &errorMsg(ostream &os, const Args&...rest)
{
return print(os, debug_rep(rest)...);
}
int main()
{
int a = 10;
double b = 10.11;
string c = "abecd";
errorMsg(cerr,a, b, c);
return 0;
}
16.57
可变参数版本的代码灵活,可以使用不同类型的参数,但是代码比较复杂,不方便调试;
原版的使用initializer_list,只能接受同一种类型元素例如string,或者是可以转换的类型。
16.58
template<typename T>
template<typename ...Args>
inline void Vec<T>::emplace_back(Args&& ...args)
{
chk_n_alloc();
alloc.construct(first_free++, forward<Args>(args)...);
}
16.59
首先chk_n_alloc()检查内存空间是否足够,不够重新分配内存空间;
然后调用construct在first_free指向的位置创建一个元素,由于s是左值,传入后由于实参为右值引用,所以叠加以后还是一个左值;
一个左值参数调用construct,将会调用拷贝构造函数创建一个string。
16.60
make_shared函数是一个可变参数模板,它会把参数转发给初始化对象的构造函数,通过动态分配内存空间创建一个指针,在最后将原始指针包装为shared_ptr返回。
16.61
template<typename T,typename ...Args>
shared_ptr<T> Make_shared(Args&&...args)
{
return shared_ptr<T>(new T(forward<Args>(args)...));
}
16.62
namespace std {
class hash<Sales_data> {
typedef size_t result_type;
typedef Sales_data argument_type;
size_t operator()(const Sales_data& s) {
return hash<string>()(s.bookNo)^hash<unsigned>()(s.units_sold)^hash<double>()(s.revenue);
}
}
}
16.63&64
#include<vector>
#include<iostream>
#include<string>
using namespace std;
template<typename T>
size_t cnt(const vector<T>&v,T t)
{
size_t res = 0;
for (auto i : v) {
if (i == t)
++res;
}
return res;
}
template<>
size_t cnt(const vector<const char *>&v, const char*t)
{
size_t res = 0;
for (auto c : v) {
if (strcmp(c, t) == 0)
++res;
}
cout << "特例化调用" << endl;
return res;
}
int main()
{
vector<int> v = { 1,2,3,4,5,6,7 };
vector<string> str = { "aaa","bbb","ccc" };
vector<const char*> cs = { "aaa","bbb","ccc" };
cout << cnt(v, 1)<<endl;
cout << cnt(str, string("aaa")) << endl;
cout << cnt(cs, "aaa") << endl;
}
16.65
template<typename T>
string Debug_rep(const char* s)
{
string ret(s);
return s;
}
template<typename T>
string Debug_rep( char* s)
{
string ret(s);
return s;
}
16.66
重载有可能会改变匹配顺序,但是不会发生匹配错误,缺点是要写的太多;
特例化不会改变顺序,但是可能发生匹配错误。
16.67
不会,因为特例化版本只是当前模板的一个独立的定义,本质是一个实例,而非函数名的一个重载版本。