C++迈向精通:模板中的引用与remove_reference原理

avatar
作者
筋斗云
阅读量:1

remove_reference 原理

模板中的引用参数

在模板中,双 '&‘ 会被解析为“引用”,这个“引用”可以是“左值”引用,也可以是“右值”引用。

例如:

template <typename T> void func(T &&a) { 	std::cout << "a = " << a << std::endl; }  int a = 123; func(a);     // 被解析为左值引用 func(123);   // 被解析为右值引用  

我们查看一下编译器是如何分析类型的:

执行:

nm -C ./a.out

可以得到如下结果:

1
这里说一下:
func(a) 对应的是 void func<int &>(int &)
func(123) 对应的是 void func<int>(int &&)

可以发现:
左值 a 被解析为 int &
右值 123 被解析为 int &&

因此,可以发现:

在模板函数(参数为双&&)中,所有模板参数都会被转换为引用

这不禁让人产生疑惑,我的模板参数明确填写了为双 & 为什么还能被转换为 单 ‘&’ 呢?

引用折叠

在C++的模板中具有一条潜规则:引用折叠。

引用折叠的意思大概就是:奇数的 & 被看作左值引用,偶数个 & 被看作右值引用。

当你想向模板函数中传入引用时,不管是左值引用还是右值引用,你都需要去写作 && 类型。

因为编译器是这样理解你的模板类型的:

首先,写成 && 会让编译器认为你这里是传入一个 引用 接下来,接下来,不管是左值还是右值,编译器都会将其解析为引用的状态:

例如:

int a = 123; // 左值,被解析为 int & 123; 				// 右值,被解析为 int && 

被解析之后的类型会被完全替换到模板参数中,例如:

template <typename T> void func(T &&a) {...} 

中的 T 会被完全替换:

void func(int & &&a) {...} // int a = 123; void func (int && &&a) {...} // 123; 

因此,可以发现 & 产生了很多个,而编译器就需要根据

奇数的 & 被看作左值引用,偶数个 & 被看作右值引用

这条原则来进行判断是左值引用还是右值引用。

remove_reference 的原理

remove_reference 是一个工具类,用于将引用类型提取出来(也就是去掉引用),例如:

template <typename T>  void func(T &&a) { 		typename std::remove_reference<T>::type c; 		... }  int main() { 		int n = 123; 		func(n);           // 这会在函数内部生成一个类型为int类型的变量c 		func(123);         // 这也会在函数内部生产一个类型为int类型的变量c } 

这东西的原理是什么呢?实际上利用了模板的特化:

template <typename T>        // 非特化 struct remove_reference { 		using type = T; };  template <typename T>         // 特化一 struct remove_reference<T &> { 		using type = T; };  template <typename T>         // 特化二 struct remove_reference<T &&> { 		using type = T; };  

是不是很简单?

通过第一个特化版本就可以将左值引用的类型提取出来,通过第二个特化版本就可以将右值引用的类型提取出来。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!