c++11 标准库函数 std::move 和 完美转发 std::forward
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <vector>
#include <map>
// C++中还有一个被广泛认同的说法,那就是可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。
// 相对于左值,右值表示字面常量、表达式、函数的非引用返回值等。
// 既然编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?
// 标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。
int a;
int &&r1 = a; // 编译失败
int &&r2 = std::move(a); // 编译通过
// 完美转发 std::forward
// 完美转发适用于这样的场景:需要将一组参数原封不动的传递给另一个函数.
// “原封不动”不仅仅是参数的值不变,在 C++ 中,除了参数值之外,还有一下两组属性:左值/右值和 const/non-const。
// 完美转发就是在参数传递过程中,所有这些属性和参数值都不能改变,同时,而不产生额外的开销,就好像转发者不存在一样。在泛型函数中,这样的需求非常普遍。
// C++11是如何解决完美转发的问题的呢?实际上,C++11是通过引入一条所谓“引用折叠”(reference collapsing)的新语言规则,并结合新的模板推导规则来完成完美转发。
typedef const int T;
typedef T & TR;
TR &v = 1; //在C++11中,一旦出现了这样的表达式,就会发生引用折叠,即将复杂的未知表达式折叠为已知的简单表达式
/*
C++11中的引用折叠规则:
TR的类型定义 声明v的类型 v的实际类型
T & TR T &
T & TR & T &
T & TR && T &
T && TR T &&
T && TR & T &
T && TR && T &&
一旦定义中出现了左值引用,引用折叠总是优先将其折叠为左值引用
*/
// C++11中,std::forward可以保存参数的左值或右值特性
#include <iostream>
using namespace std;
template <typename T> void process_value(T & val)
{
cout << "T &" << endl;
}
template <typename T> void process_value(T && val)
{
cout << "T &&" << endl;
}
template <typename T> void process_value(const T & val)
{
cout << "const T &" << endl;
}
template <typename T> void process_value(const T && val)
{
cout << "const T &&" << endl;
}
//函数 forward_value 是一个泛型函数,它将一个参数传递给另一个函数 process_value
template <typename T> void forward_value(T && val) //参数为右值引用
{
process_value( std::forward<T>(val) ); // C++11中,std::forward可以保存参数的左值或右值特性
}
void mytest()
{
int a = 0;
const int &b = 1;
forward_value(a); // T &
forward_value(b); // const T &
forward_value(2); // T &&
forward_value( std::move(b) ); // const T &&
return;
}
int main()
{
mytest();
system("pause");
return 0;
}