c++Primer——第十六章:模板与泛型编程

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!")中:

  • 推断出T的类型为string
  • 因此,remove_reference用string进行实例化
  • remove_reference的type类型是string
  • move的返回类型是string&&

因此这个调用实例化move 即 string&& move(string&& t),函数体返回static_cast(t),由于t的类型已经是string&&,于是类型转换什么都不做,因此,此调用的结果就是它锁接受的右值引用

        在第二个赋值中,传递给move的实参是个左值

  • 推断出T的类型为string&
  • 因此,remove_reference用string&进行实例化
  • remove_reference的type是string
  • move的返回类型仍然是string&&
  • move的函数参数t实例化为string& &&, 折叠为string&

因此,这个调用实例化move,即string&& move(string&),这正是我们所寻求的——我们希望将一个右值应用绑定到一个左值(是不是可以理解为将一个左值转换为一个右值引用)

函数体返回static_cast(t), t的类型是string&, cast将其转为string&&;

8、std::forward

std::forward能够保持原始实参的属性,std::forward()不仅可以保持左值或者右值不变,同时还可以保持const、Lreference、Rreference、validate等属性不变

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);
}

你可能感兴趣的:(c++Primer,c++)