在讲解move和forward之前需要了解左值和右值。具体可看我前面的博客:
https://blog.csdn.net/King_weng/article/details/100569377
C++11参数类型推导过程中的三个原则:
(1)引用折叠原则
注:
助记:T后有两个或三个引用符时折叠为一个,后面为四个引用符时才折叠为两个。
T为具体类型,非推导类型:
T&& 为右值引用只有当 T 为一个具体的类型时才成立,而如果 T 是推导类型时(如模板参数, auto 等)这就不一定了,比如说如下代码中的 ref_int,根据定义这个变量的类型必定是一个右值引用,但模板函数 func 的参数 arg 则不定是右值引用了,因为此时 T 是一个推导类型。
int&& ref_int = get_int();
template
void func(T&& arg)
{
}
(2)右值引用的特殊类型推断原则(模板推导原则)
当将一个左值传递给一个参数为右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参类型为实参的左值引用,例:
template
void f; // T指向模板的右值引用
int i = 22; // int i为左值
f(i) // 推断左值((int i)为左值的引用(int& i)
则上诉模板参数类型T将推断为int&类型,而非int类型。从中可以得出结论:如果一个函数形参是一个指向模板类型的右值引用,则该参数可以被绑定到一个左值上。
(3)使用static_cast显示将左值转换为右值
虽然不能隐式的将一个左值转换为右值引用,但是可以通过static_cast显示地将一个左值转换为一个右值。【C++11中为static_cast新增的转换功能】
template
typename remove_reference::type&&
std::move(T&& a)
{
typedef typename remove_reference::type&& RvalRef;
return static_cast(a);
}
}
功能:
根据模板推导原则和引用折叠原则,无论是给move传递了一个左值还是右值,最终返回的,都是一个右值引用。而这正是 move 的意义,得到一个右值的引用。实际上直接用 static_cast 也是能达到同样的效果,从move的定义可以看出,move自身除了做一些参数的推断之外,返回右值引用本质上还是靠static_cast
注:右值引用变量的名称是左值,而若要绑定到接受右值引用参数的重载,就必须转换到亡值(函数调用或重载运算符表达式,其返回类型为对象的右值引用),此乃移动构造函数与移动赋值运算符典型地使用 std::move 的原因。
例:
class Foo
{
public:
string member;
// 复制 member.
Foo(const std::string& m): member(m) {}
// 移动 member.
Foo(std::string&& m): member(std::move(m)) {}
};
上述Foo(std::string&& member)中的member是rvalue reference,但是member却是一个左值lvalue,因此在初始化列表中需要使用std::move将其转换成rvalue。
3、std::forward()
template< class T >
constexpr T&& forward( typename std::remove_reference::type&& t ) noexcept;
功能:
接受一个参数,然后返回该参数本来所对应的类型的引用。实现了参数在传递过程中保持其值属性的功能,即若是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值,即实现了完美转发。
例:
class Foo
{
public:
std::string member;
template
Foo(T&& member): member{std::forward(member)} {}
};
1)传递一个lvalue或者传递一个const lvaue :
2)传递一个rvalue:
(1)std::move执行到右值的无条件转换,std::forward执行到右值的有条件转换,在参数都是右值时,二者就是等价的。其实std::move和std::forward就是在C++11基本规则之上封装的语法糖。
(2)std::move和std::forward只不过就是执行类型转换的两个函数;std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有做任何事情。它们没有产生需要执行的代码,一byte都没有。
(3)std::forward
例:
#include
#include
#include
#include
using namespace std;
struct A
{
A(int&& n)
{
cout << "rvalue overload, n=" << n << endl;
}
A(int& n)
{
cout << "lvalue overload, n=" << n << endl;
}
};
class B
{
public:
template
B(T1 && t1, T2 && t2, T3 && t3) :
a1_(std::forward(t1)),
a2_(std::forward(t2)),
a3_(std::forward(t3))
{
}
private:
A a1_, a2_, a3_;
};
template
std::unique_ptr make_unique1(U&& u)
{
//return std::unique_ptr(new T(std::forward(u)));
return std::unique_ptr(new T(std::move(u)));
}
template
std::unique_ptr make_unique2(U&&... u)
{
//return std::unique_ptr(new T(std::forward(u)...));
return std::unique_ptr(new T(std::move(u)...));
}
int main()
{
auto p1 = make_unique1(2);
int i = 10;
auto p2 = make_unique1(i);
int j = 100;
auto p3 = make_unique2(i, 2, j);
system("pause");
return 0;
}
forward时结果:
move时结果: