C++ Primer课后习题解答(第十六章)
Exercises Section 16.1.1
Ex16.1
编译器用推断出的模板参数生成一个特定版本的函数,这个过程就叫做实例化。
Ex16.2
#include<iostream>
#include<functional>
using namespace std;
template <typename T> int compare(const T &v1, const T &v2)
{
if (less<T>() (v1, v2))
return -1;
if (less<T>() (v2, v1))
return 1;
return 0;
}
int main()
{
int a, b;
cin >> a >> b;
cout << compare(a, b) << endl;
system("pause");
return 0;
}
Ex16.3
如果你的 compare 函数使用的 < 操作符,那么实例化 Sales_data 会发生错误,因为 Sales_data 未定义 < 操作符。
Ex16.4
#include<iostream>
#include<vector>
#include<list>
#include<string>
using namespace std;
template <typename T, typename U>
T find(T beg, T end, U val)
{
auto iter = beg;
while (iter != end)
{
if (*iter == val)
break;
++iter;
}
return iter;
}
int main()
{
vector<int> vec = {1, 2, 3, 4, 5, 6};
list<string> ls = {"hello", "world", "hi"};
int value;
string word;
cin >> value >> word;
auto p1 = find(vec.begin(), vec.end(), value);
if (p1 != vec.end())
cout << *p1 << endl;
auto p2 = find(ls.begin(), ls.end(), word);
if (p2 != ls.end())
cout << *p2 << endl;
system("pause");
return 0;
}
Ex16.5
#include<iostream>
using namespace std;
template<typename T, unsigned N> void print(const T (&arr)[N])
{
for (auto v : arr)
cout << v << " ";
}
int main()
{
int a[] = {1, 2, 3, 4, 5};
string b[] = {"hello", "world"};
print(a);
cout << endl;
print(b);
cout << endl;
system("pause");
return 0;
}
Ex16.6
#include<iostream>
using namespace std;
template<typename T, unsigned N> T* begin(const T (&arr)[N])
{
return &arr[0];
}
template<typename T, unsigned N> T* end(const T (&arr)[N])
{
return &arr[N - 1];
}
int main()
{
int a[] = {1, 2, 3, 4, 5};
string b[] = {"hello", "world"};
for (auto p = begin(a); p != end(a); ++p)
cout << *p << " ";
cout << endl;
for (auto p = begin(b); p != end(b); ++p)
cout << *p << " ";
cout << endl;
system("pause");
return 0;
}
Ex16.7
#include<iostream>
using namespace std;
template<typename T, unsigned N> constexpr unsigned arr_size(const T (&arr)[N])
{
return N;
}
int main()
{
int a[] = {1, 2, 3, 4, 5};
string b[] = {"hello", "world"};
cout << arr_size(a) << endl;
cout << arr_size(b) << endl;
system("pause");
return 0;
}
Ex16.8
支持 != 操作的类比支持 < 操作的类更多,简而言之,就是 != 操作更通用。
Exercises Section 16.1.2
Ex16.9
函数模板:编译器能够根据推断出的模板参数类型实例化一个特定类型的函数。
类模板:编译器不能推断出模板参数类型,而是需要在<>内的模板名字后面补充信息,然后实例化一个特定类型的类。
Ex16.10
当实例化一个类模板时,编译器重写该类模板,通过给定的类型参数取代所有出现模板参数的地方。
Ex16.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<elemType> *ptr, elemType value);
private:
ListItem<elemType> *front, *end;
};
Ex16.12
template <typename> class BlobPtr;
template <typename T> bool operator==(const Blob<T> &, const Blob<T> &);
template <typename T> bool operator!=(const Blob<T> &, const Blob<T> &);
template <typename T> class Blob
{
friend class BlobPtr<T>;
friend bool operator==<T>(const Blob<T> &, const Blob<T> &);
friend bool operator!=<T>(const Blob<T> &, const Blob<T> &);
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
Blob();
Blob(std::initializer_list<T> il);
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->push_back(std::move(t)); }
void pop_back();
T& back();
const T& back() const;
T& operator[](size_type i);
const T& operator[](size_type i) const;
private:
std::shared_ptr<std::vector<T>> data;
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> const T& Blob<T>::back() const
{
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> T& Blob<T>::operator[](size_t i) const
{
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> Blob<T>::Blob(): data(std::make_shared<std::vector<T>>()) { }
template <typename T> Blob<T>::Blob(std::initializer_list<T> il): data(std::make_shared<std::vector<T>> il) { }
template <typename T> bool operator==(const Blob<T> &lhs, const Blob<T> &rhs)
{
if (lhs.size() != rhs.size())
return false;
for (size_t i = 0; i != lhs.size(); ++i)
{
if (lhs[i] != rhs[i])
return false;
}
return true;
}
template <typename T> bool operator!=(const Blob<T> &lhs, const Blob<T> &rhs)
{
return !(lhs == rhs);
}
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
{
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++();
BlobPtr & operator--();
BlobPtr operator++(int);
BlobPtr operator--(int);
private:
std::shared_ptr<std::vector<T>> check(std::size_t, const std::string &) const;
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr;
};
template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int)
{
BlobPtr ret = *this;
++*this;
return ret;
}
template <typename T> BlobPtr<T> BlobPtr<T>::operator--(int)
{
BlobPtr ret = *this;
--*this;
return ret;
}
template <typename T> bool operator==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
return lhs.wptr.lock().get() == rhs.wptr.lock().get() && lhs.curr == rhs.curr;
}
template <typename T> bool operator!=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
return !(lhs == rhs);
}
Ex16.13
由于函数模板的实例化只处理特定类型,因此对于相等和关系运算符,对每个 BlobPtr 实例和用相同类型实例化的运算符建议一对一的友好关系即可。
Ex16.14
template <int H, int W> class Screen
{
public:
Screen(): contents(H * W, ' ') { }
Screen(char c): contents(H * W, c) { }
char get() const { return contents[cursor]; }
inline char get(int, int) const;
Screen &move(int, int);
Screen &set(char);
Screen &display(ostream &os)
{
do_display(os);
return *this;
}
const Screen &display(ostream &os) const
{
do_display(os);
return *this;
}
private:
int cursor = 0;
string contents;
void do_display(ostream &os) const { os << contents; }
};
template <int H, int W> inline char Screen<H, W>::get(int r, int c) const
{
int row = r * W;
return contents[row + c];
}
template <int H, int W> inline Screen<H, W> &Screen<H, W>::set(char c)
{
contents[cursor] = c;
return *this;
}
template <int H, int W> inline Screen<H, W> &Screen<H, W>::move(int r, int c)
{
cursor = r * W + c;
return *this;
}
Ex16.15
template <int H, int W> istream & operator>>(istream &, Screen<H, W> &);
template <int H, int W> ostream & operator<<(ostream &, Screen<H, W> &);
template <int H, int W> class Screen
{
friend istream & operator>><H, W>(istream &, Screen<H, W> &);
friend ostream & operator<<<H, W>(ostream &, Screen<H, W> &);
// 其他成员不变
};
template <int H, int W> istream & operator>>(istream &is, Screen<H, W> &sc)
{
string s;
is >> s;
sc.contents = s.substr(0, H * W);
}
template <int H, int W> ostream & operator<<(ostream &os, Screen<H, W> &sc)
{
os << sc.contents;
return os;
}
Ex16.16
template <typename T> class Vec
{
public:
Vec(): elements(nullptr), first_free(nullptr), cap(nullptr) { }
Vec(const Vec&);
Vec(const T*, const T*);
Vec(initializer_list<T> lst);
Vec& operator=(const Vec&);
~Vec();
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) { if (n > capacity()) resize(n); }
void resize(size_t);
T& operator[](size_t n) { return elements[n]; }
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> std::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)
{
T *data = alloc.allocate(e - b);
return make_pair(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<T> &s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = newdata.second;
cap = newdata.second;
}
template <typename T> Vec<T>::Vec(const T *b, const T *e)
{
pair<T*, T*> newdata = alloc_n_copy(b, e);
elements = newdata.first;
first_free = cap = newdata.second;
}
template <typename T> Vec<T>::Vec(initializer_list<T> lst)
{
auto newdata = alloc_n_copy(lst.begin(), lst.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 &rhs)
{
auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = newdata.first;
first_free = newdata.second;
cap = newdata.second;
return *this;
}
template <typename T> void Vec<T>::reallocate()
{
auto newcapacity = size() ? size() * 2 : 1;
auto newdata = alloc.allocate(newcapacity);
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 + newcapacity;
}
template <typename T>
void Vec<T>::resize(size_t n)
{
if (n > size())
{
while (n > size())
push_back("");
}
else if (n < size())
{
while (size() > n)
alloc.destroy(--first_free);
}
}
Exercises Section 16.1.3
Ex16.17
在模板参数列表中,typename 和 class 含义相同,typename 比 class 更直观,因为内置类型也可以作为模板类型参数。当使用作用域运算符::
访问一个模板类型参数的成员时,该成员可能为类型或者静态成员,为了区分这点,规定使用 typename 指明其是一个类型。
Ex16.18
a) template <typename T, U, typename V> void f1(T, U, V); // 非法,U是类型参数,必须有关键字 typename
// 修改如下:
template <typename T, typename U, typename V> void f1(T, U, V);
b) template <typename T> T f2(int &T); // 非法,T不能作为参数名
// 修改如下:
template <typename T> T f2(T &);
c) inline template <typename T> T foo(T, unsigned int*); // 非法,inline 位置错误
// 修改如下:
template <typename T> inline T foo(T, unsigned int*);
d) template <typename T> f4(T, T); // 非法,没有返回类型
// 修改如下:
template <typename T> T f4(T, T);
e) typedef char Ctype;
template <typename Ctype> Ctype f5(Ctype a); // 合法,但是模板声明的 Ctype 隐藏了类型别名 Ctype
Ex16.19
#include<iostream>
#include<vector>
using namespace std;
template <typename T> void print(const T &container)
{
for (typename T::size_type i = 0; i != container.size(); ++i)
cout << container.at(i) << endl;
}
int main()
{
vector<int> vec = {1, 2, 3, 4, 5};
print(vec);
system("pause");
return 0;
}
Ex16.20
#include<iostream>
#include<vector>
using namespace std;
template <typename T> void print(const T &container)
{
for (auto it = container.begin(); it != container.end(); ++it)
cout << *it << endl;
}
int main()
{
vector<int> vec = {1, 2, 3, 4, 5};
print(vec);
system("pause");
return 0;
}
Exercises Section 16.1.4
Ex16.21
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;
};
Ex16.22
class TextQuery
{
using line_no = vector<string>::size_type;
public:
TextQuery(ifstream &in);
// 其他成员不变
};
TextQuery::TextQuery(ifstream &in): file(new vector<string>, DebugDelete())
{
//...
}
Ex16.23
在程序结束时释放 shared_ptr 指向的资源,会在程序结束时看到打印语句。
Ex16.24
template <typename T> class Blob
{
public:
template <typename It> Blob(It b, It e);
// 其他成员不变
};
template <typename T> template <typename It> Blob<T>::Blob(It b, It e): data(make_shared<vector<T>>(b, e)) { }
Exercises Section 16.1.5
Ex16.25
extern template class vector<string>; // extern 表明不在本文件中生成实例化代码,该实例化的定义会在其他文中中。
template class vector<Sales_data>; // 用 Sales_data 类实例化作为 vector 元素类型,在其他文件中可用 extern 声明此实例化,使用该定义。
Ex16.26
不可以; 当显式实例化 vector
Ex16.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)、(f)分别进行了 Stack 对 char、double、int、string 类型的实例化,因为这些语句都需要用到其实例化的类;(d)和 (e)未发生实例化,因为在上述对应代码前就已经发生了实例化。
Exercises Section 16.2.1
Ex16.32
在模板参数推断阶段,编译器使用函数调用过程中的参数类型推断模板参数,找出最为匹配给定的函数调用的函数版本。
Ex16.33
- 可以将一个 非 const 对象的引用或指针传递给一个 const 的引用或指针
- 数组或者函数指针转换;函数形参不是引用类型,则可对数组或函数类型的实参应用正常的指针转换
Ex16.34
都不合法,当模板函数形参为引用类型,数组不能转换为指针。
Ex16.35
template <typename T> T calc(T, int);
template <typename T> fcn(T, T);
double d; float f; char c;
a) calc(c, 'c'); // 合法,T 为 char
b) calc(d, f); // 合法,T 为 double
c) fcn(c, 'c'); // 合法,T 为 char
d) fcn(d, f); // 非法,无法实例化 fcn(double, float)
Ex16.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); // T 为 int *
b) f2(p1, p2); // T1 为 int *,T2为 int *
c) f1(cp1, cp2); // T 为 const int *
d) f2(cp1, cp2); // T1 为 const int *,T2 为 const int *
e) f1(p1, cp1); // T 为 int *
f) f2(p1, cp1); // T1 为 int *,T2 为 const int *
Exercises Section 16.2.2
Ex16.37
可以;不过需要显式指定参数类型,如max<double>
Ex16.38
使用 make_shared 时,编译器无法从已有参数推断模板参数类型,这时就要显式指定模板参数类型。
Ex16.39
compare<string>("s1", "s2");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端