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

本文详细探讨了C++模板的使用,包括class和typename关键字在模板参数中的等价性,延迟实例化,模板类型成员的访问,以及extern模板的控制。此外,还解析了std::move和std::forward的工作原理,以及模板重载的选择策略。通过示例代码,阐述了模板在实际编程中的应用和注意事项。
摘要由CSDN通过智能技术生成

1、在模板的类型参数前必须使用关键字class或typename,在模板参数列表中这两个关键字的含义相同,可以互换使用。

2、默认情况下,对于一个实例化了的类模板,其成员只有在使用时才会被实例化

3、在一个类模板的作用域内,我么可以直接使用模板名而不必指定模板实参

4、在普通代码中,由于编译器掌握类的定义,因此它知道通过作用域运算符访问的名字是类型还是static成员,但对于模板代码就存在困难。如:T::mem,编译器并不知道mem是类型还是static成员。c++默认通过作用域运算符访问的不是类型,因此当我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是个类型,通过使用关键字typename来实现

    template<typename T>
	typename T::value_type f(){}

当我们希望通知编译器一个名字表示类型是,必须使用关键字typename,而不能用class

5、控制实例化

模板被使用是才会进行实例化,意味着相同的实例可能出现在多个对象文件中。在新标准中可以通过显式实例化来避免这种开销。

	extern template class Blob<string>; //声明
	template int compare(const int&, const int&); //定义

当编译器遇到extern模板声明时,它不会再本文件中生成实例化代码。由于i编译器在使用一个模板是自动对其实例化,因此extern声明必须出现在任何使用此实例化版本的代码前

6、习题6.34 对下面得代码解释每个调用是否合法

template<class T>
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 T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::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<string>的type类型是string
  • move的返回类型是string&&

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

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

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

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

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

8、std::forward

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

template <typename F, typename T1, typename T2>
void flip(F f, T1&& t1, T2&& t2)
{
	f(std::forward<T1>(t1), std::forward<T2>(t2));
}

如果我们调用flip(g, i, 42), 则i将以int& 类型传递给g,42将以int&&类型传递给g

9、重载与模板

如果有多个函数(包括模板函数)提供同样好的匹配

  • 如果同样好的函数中只有一个是非模板函数,选择它
  • 如果同样好的函数中没有非模板函数,选择更特例化的那个
  • 否则,调用歧义
template<typename T>
string debug_rep(const T& t) {/* ... 1号 */ }

template<typename T>
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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值