16.1 给出实例化的定义。
答:
当编译器实例化一个模版时,它使用实际的模版参数代替对应的模版参数来创建出模版的一个新“实例”。
16.2 编写并测试你自己版本的 compare
函数。
答:
16.4 编写行为类似标准库 find
算法的模版。函数需要两个模版类型参数,一个表示函数的迭代器参数,另一个表示值的类型。使用你的函数在一个 vector<int>
和一个list<string>
中查找给定值。
答:
16.5 为6.2.4节中的print
函数编写模版版本,它接受一个数组的引用,能处理任意大小、任意元素类型的数组。
答:
16.6 你认为接受一个数组实参的标准库函数 begin
和 end
是如何工作的?定义你自己版本的 begin
和 end
。
答:
16.7 编写一个 constexpr
模版,返回给定数组的大小。
答:
16.8 在第97页的“关键概念”中,我们注意到,C++程序员喜欢使用 !=
而不喜欢 <
。解释这个习惯的原因。
答:
因为大多数类只定义了 !=
操作而没有定义 <
操作,使用 !=
可以降低对要处理的类型的要求。
16.9 什么是函数模版,什么是类模版?
答:
一个函数模版就是一个公式,可用来生成针对特定类型的函数版本。类模版是用来生成类的蓝图的,与函数模版的不同之处是,编译器不能为类模版推断模版参数类型。如果我们已经多次看到,为了使用类模版,我们必须在模版名后的尖括号中提供额外信息。
16.10 当一个类模版被实例化时,会发生什么?
答:
一个类模版的每个实例都形成一个独立的类。
16.11 下面 List
的定义是错误的。应如何修改它?
答:
16.12 编写你自己版本的 Blob
和 BlobPtr
模版,包含书中未定义的多个const
成员。
答:
16.14 编写 Screen
类模版,用非类型参数定义 Screen
的高和宽。
答:
16.16 将 StrVec
类重写为模版,命名为 Vec
。
答:
16.17 声明为 typename
的类型参数和声明为 class
的类型参数有什么不同(如果有的话)?什么时候必须使用typename
?
答:
没有什么不同。仅当我们希望通知编译器一个名字表示类型时,必须使用关键字 typename
,而不能使用 class
。
16.18 解释下面每个函数模版声明并指出它们是否非法。更正你发现的每个错误。
答:
- (a) 非法。应该为
template <typename T, typename U, typename V> void f1(T, U, V);
。 - (b) 非法。应该为
template <typename T> T f2(int &t);
- (c) 非法。应该为
template <typename T> inline T foo(T, unsigned int*);
- (d) 非法。应该为
template <typename T> T f4(T, T);
- (e) 非法。
Ctype
被隐藏了。
16.19 编写函数,接受一个容器的引用,打印容器中的元素。使用容器的 size_type
和 size
成员来控制打印元素的循环。
答:
16.20 重写上一题的函数,使用begin
和 end
返回的迭代器来控制循环。
答:
16.21 编写你自己的 DebugDelete
版本。
答:
16.25 解释下面这些声明的含义。
答:
前者是模版声明,后者是实例化定义。
16.33 指出在模版实参推断过程中允许对函数实参进行的两种类型转换。
答:
const
转换:可以将一个非const
对象的引用(或指针)传递给一个const
的引用(或指针)形参。- 数组或函数指针转换:如果函数形参不是引用类型,则可以对数组或函数类型的实参应用正常的指针转换。一个数组实参可以转换为一个指向其首元素的指针。类似的,一个函数实参可以转换为一个该函数类型的指针。
16.34 对下面的代码解释每个调用是否合法。如果合法,T
的类型是什么?如果不合法,为什么?
答:
- (a) 不合法。
compare(const char [3], const char [6])
, 两个实参类型不一致。 - (b) 合法。
compare(const char [4], const char [4])
.
16.35 下面调用中哪些是错误的(如果有的话)?如果调用合法,T
的类型是什么?如果调用不合法,问题何在?
答:
- (a) 合法,类型为
char
- (b) 合法,类型为
double
- (c) 合法,类型为
char
- (d) 不合法,这里无法确定T的类型是
float
还是double
16.36
进行下面的调用会发生什么:
答:
(a) f1(int*, int*);
(b) f2(int*, int*);
(c) f1(const int*, const int*);
(d) f2(const int*, const int*);
(e) f1(int*, const int*); 这个使用就不合法
(f) f2(int*, const int*);
16.38 当我们调用 make_shared
时,必须提供一个显示模版实参。解释为什么需要显式模版实参以及它是如果使用的。
答:
如果不显示提供模版实参,那么 make_shared
无法推断要分配多大内存空间。
16.40 下面的函数是否合法?如果不合法,为什么?如果合法,对可以传递的实参类型有什么限制(如果有的话)?返回类型是什么?
答:
合法。该类型需要支持 +
操作。
16.41 编写一个新的 sum
版本,它返回类型保证足够大,足以容纳加法结果。
答:
16.42 对下面每个调用,确定 T
和 val
的类型:
答:
(a) int&
(b) const int&
(c) int&&
16.48 编写你自己版本的 debug_rep
函数。
答:
16.49 解释下面每个调用会发生什么:
答:
g(42); //g(T )
g(p); //g(T*)
g(ci); //g(T)
g(p2); //g(T*)
f(42); //f(T)
f(p); //f(T)
f(ci); //f(T)
f(p2); //f(const T*)
16.51 调用本节中的每个 foo
,确定 sizeof...(Args)
和 sizeof...(rest)
分别返回什么。
答:
16.53 编写你自己版本的 print
函数,并打印一个、两个及五个实参来测试它,要打印的每个实参都应有不同的类型。
答:
16.61 定义你自己版本的 make_shared
。
答:
16.63 定义一个函数模版,统计一个给定值在一个vecor
中出现的次数。测试你的函数,分别传递给它一个double
的vector
,一个int
的vector
以及一个string
的vector
。
答:
16.65 在16.3节中我们定义了两个重载的 debug_rep
版本,一个接受 const char*
参数,另一个接受 char *
参数。将这两个函数重写为特例化版本。
答: