1、在模板的类型参数前必须使用关键字class或typename,在模板参数列表中这两个关键字的含义相同,可以互换使用。
2、默认情况下,对于一个实例化了的类模板,其成员只有在使用时才会被实例化
3、在一个类模板的作用域内,我么可以直接使用模板名而不必指定模板实参
4、在普通代码中,由于编译器掌握类的定义,因此它知道通过作用域运算符访问的名字是类型还是static成员,但对于模板代码就存在困难。如:T::mem,编译器并不知道mem是类型还是static成员。c++默认通过作用域运算符访问的不是类型,因此当我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是个类型,通过使用关键字typename来实现
template
typename T::value_type f(){}
当我们希望通知编译器一个名字表示类型是,必须使用关键字typename,而不能用class
5、控制实例化
模板被使用是才会进行实例化,意味着相同的实例可能出现在多个对象文件中。在新标准中可以通过显式实例化来避免这种开销。
extern template class Blob; //声明
template int compare(const int&, const int&); //定义
当编译器遇到extern模板声明时,它不会再本文件中生成实例化代码。由于i编译器在使用一个模板是自动对其实例化,因此extern声明必须出现在任何使用此实例化版本的代码前
6、习题6.34 对下面得代码解释每个调用是否合法
template
int com(const T&, const T&);
compare("hi", "world"); //不合法 compare(const char[3], const char[6]) 两个实参类型不一致
compare("abc", "ffd"); //合法 compare(const char[4], const char[4])
7、理解std::move
template
typename remove_reference::type&& move(T&& t)
{
return static_cast::type&&>(t);
}
move的参数T&&是一个指向模板类型参数的右值引用,通过引用折叠,此参数可以与任何类型的实参匹配。特别的,可以传递给move一个左值,也可以传递一个右值。
string s1("hi!"), s2;
s2 = std::move(string("bye!")); //正确 从一个右值移动数据
s2 = std::move(s1); //正确 但赋值后 s1的值是不确定的
分析下上面代码看std::move是如何工作的。
在第一个赋值中,传递给move的实参是string的构造函数的右值结果——string("bye!")。当向一个右值引用函数参数传递一个右值时,由实参推断出的类型是被引用的类型,因此在std::move("bye!")中:
因此这个调用实例化move
在第二个赋值中,传递给move的实参是个左值
因此,这个调用实例化move
函数体返回static_cast
8、std::forward
std::forward能够保持原始实参的属性,std::forward
template
void flip(F f, T1&& t1, T2&& t2)
{
f(std::forward(t1), std::forward(t2));
}
如果我们调用flip(g, i, 42), 则i将以int& 类型传递给g,42将以int&&类型传递给g
9、重载与模板
如果有多个函数(包括模板函数)提供同样好的匹配
template
string debug_rep(const T& t) {/* ... 1号 */ }
template
string debug_rep(T* p){/* ... 2号 */}
void test()
{
string s("hi");
//参数是个对象 只有1号匹配
debug_rep(s);
//两个都可行 1号实例化为 debug_rep(const string*&)
//2号实例化为 debug_rep(string*)
//可以看到2号是精确匹配,1号还有个普通指针到const指针的转换
debug_rep(&s);
const string* sp = &s;
//两个都可行 1号实例化为 debug_rep(const string*&)
//2号实例化为debug_rep(const string*)
//但是2号更特例化(2号只能用于指针,1号更通用)所以调用2号
debug_rep(sp);
}