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
可以得到如下结果:

 这里说一下:
 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类型的变量cfunc(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;
};是不是很简单?
通过第一个特化版本就可以将左值引用的类型提取出来,通过第二个特化版本就可以将右值引用的类型提取出来。