练习16.1
给出实例化的定义。
解答:
实例化,就是编译器将一个函数模板中的类型用一个具体类型替换的过程。
【引用百度百科】在面向对象的编程中,通常把用类创建对象的过程称为实例化。
练习16.2
编写并测试你自己版本的compare函数。
解答:
这个函数模板的实现,可以参考书中的实现。
不过这里要注意的是,当你的实现中涉及到比较运算符的时候,需要确定你使用的类型支持比较运算符。
练习16.3
对两个Sales_data对象调用你的compare函数,观察编译器在实例化过程中如何处理错误。
解答:
这个其实就是使用没有比较运算符的类进行比较。
#include <functional>
#include <iostream>
class A{
public:
A() = default;
A(const int& n):num(n){}
int num;
};
template<typename T>
int compare(const T& a, const T& b){
if(std::less<T>()(a, b)){
return 1;
} else if(std::less<T>()(b, a)){
return -1;
} else {
return 0;
}
}
int main(){
int ia(10), ib(12);
std::cout << compare(ia, ib) << std::endl;
A a(10), b(12);
std::cout << compare(a, b) << std::endl;
}
这里就没有使用Sales_data类。自行定义了一个没有比较操作符的类。
然后,可以看一下编译器那边会给出什么提示。
我这边用的g++4.9.1出现的错误信息比较长,不过意思都差不多,就是没有找到‘<’这个比较运算符。
练习16.4
编写行为类似标准库find算法的模板。函数需要两个模板类型参数,一个表示函数的迭代器参数,另一个表示值的类型。使用你的函数再一个vector<int>和一个list<string>中查找给定值。
解答:
#include <iostream>
#include <iterator>
#include <vector>
#include <list>
template<typename Iterator, typename T>
Iterator find(const Iterator& begin, const Iterator& end, const T &element){
for ( Iterator it = begin; it != end; ++it){
if (*it == element){
return it;
}
}
return end;
}
int main(){
int num[] = {1,2,3,4,5,6,7,8,9,10};
std::vector<int> vint(std::begin(num), std::end(num));
std::list<int> lint(std::begin(num), std::end(num));
auto iit = find(vint.cbegin(), vint.cend(), 2);
std::cout << *iit << std::endl;
auto lit = find(lint.cbegin(), lint.cend(), 3);
std::cout << *lit << std::endl;
}
练习16.5
为6.2.4节(第195页)中的print函数编写模板版本,它接受一个数组和引用,能处理任意大小、任意元素类型的数组。
解答:
#include <iostream>
#include <string>
template <typename T, std::size_t Num>
void print(T (&arr)[Num]){
for (auto elem : arr){
std::cout << elem << std::endl;
}
}
int main(){
int num[] = {1,2,3};
std::string str[] = {"one", "two", "three"};
print(num);
print(str);
}
练习16.6
你认为接受一个数组实参的标准库函数begin和end是如何工作的?定义你自己版本的begin和end。
解答:
#include <iostream>
template <typename T, std::size_t Num>
T* begin(T (&Container)[Num]){
return Container;
}
template <typename T, std::size_t Num>
T* end(T (&Container)[Num]){
return (Container + Num);
}
int main(){
int num[] = {1,2,3,4,5,6};
std::cout << begin(num) <<": val is " << *begin(num) << std::endl;
std::cout << end(num) - 1 <<": val is " << *(end(num) - 1) << std::endl;
std::cout << end(num) << std::endl; //将打印最后一个元素后面的的地址
}
练习16.7
编写一个constexpr模板,返回给定数组的大小。
解答:
#include <iostream>
template <typename T, std::size_t Num>
size_t cconstexpr(T (&arr)[Num]){
return Num;
}
int main(){
int num[] = {1,2,3,4,5,6,7,8};
std::cout << cconstexpr(num) << std::endl;
}
在c++11标准中constexpr已经是关键字了,所以不能使用其作为函数的名字,所以这里多加了一个c。
不过,在编译的时候将-std=c++11这个选项去掉,使用constexpr作为函数名就没有问题。
在VS2013中,应该也有对应C++11的选项,不过现在的环境没有VS2013,所以这个可能要晚些时候补上了。
练习16.8
再第97页的“关键概念”中,我们注意到,C++程序员喜欢用!=而不喜欢<。解释这个习惯的原因。
解答:
这里就是区别C和C++的地方,C++主打面向对象编程,而C是面向过程编程。
C++中编程的核心逐渐向不同类型的对象进行转移,这些对象不一定支持小于的操作,而不等于的操作更为方便使用。
同样,使用不等于也可以减少很多不必要的实现,从而能让泛型编程的代码更加优美。
C的话,大多数情况下都是对内置类型进行操作,所以在小于和不等于的选择中,肯定选择比较好理解的那个。
当然这里还是有历史原因的,教课书编写者的一些编程习惯,也会潜移默化的影响读者。