C++11之std::move()和std::forward()

1、必备知识

在讲解move和forward之前需要了解左值和右值。具体可看我前面的博客:

https://blog.csdn.net/King_weng/article/details/100569377

 

C++11参数类型推导过程中的三个原则:

(1)引用折叠原则

  • T& & (引用的引用) 被转化成 T&.
  • T&& & (rvalue的引用)被传化成 T&.
  • T& && (引用作rvalue) 被转化成 T&.
  • T&& && 被转化成 T&&

注:

助记:T后有两个或三个引用符时折叠为一个,后面为四个引用符时才折叠为两个。

T为具体类型,非推导类型:

        T&& 为右值引用只有当 T 为一个具体的类型时才成立,而如果 T 是推导类型时(如模板参数, auto 等)这就不一定了,比如说如下代码中的 ref_int,根据定义这个变量的类型必定是一个右值引用,但模板函数 func 的参数 arg 则不定是右值引用了,因为此时 T 是一个推导类型。

int&& ref_int = get_int();

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

(2)右值引用的特殊类型推断原则(模板推导原则)

        当将一个左值传递给一个参数为右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参类型为实参的左值引用,例:

template<typename T>
void f<T&&>;	// T指向模板的右值引用

int i = 22;		// int i为左值
f(i)			// 推断左值((int i)为左值的引用(int& i)

则上诉模板参数类型T将推断为int&类型,而非int类型。从中可以得出结论:如果一个函数形参是一个指向模板类型的右值引用,则该参数可以被绑定到一个左值上。

(3)使用static_cast显示将左值转换为右值

        虽然不能隐式的将一个左值转换为右值引用,但是可以通过static_cast显示地将一个左值转换为一个右值。【C++11中为static_cast新增的转换功能】

 

2、std::move()

template<class T> 
typename remove_reference<T>::type&&
std::move(T&& a)
{
  typedef typename remove_reference<T>::type&& RvalRef;
  return static_cast<RvalRef>(a);
}

}

功能:

        根据模板推导原则和引用折叠原则,无论是给move传递了一个左值还是右值,最终返回的,都是一个右值引用。而这正是 move 的意义,得到一个右值的引用。实际上直接用 static_cast 也是能达到同样的效果,从move的定义可以看出,move自身除了做一些参数的推断之外,返回右值引用本质上还是靠static_cast<T&&>完成的。

        注:右值引用变量的名称是左值,而若要绑定到接受右值引用参数的重载,就必须转换到亡值(函数调用或重载运算符表达式,其返回类型为对象的右值引用),此乃移动构造函数与移动赋值运算符典型地使用 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<T>::type&& t ) noexcept;

功能:

         接受一个参数,然后返回该参数本来所对应的类型的引用。实现了参数在传递过程中保持其值属性的功能,即若是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值,即实现了完美转发。

例:

class Foo
{
public:
    std::string member;

    template<typename T>
    Foo(T&& member): member{std::forward<T>(member)} {}
};

1)传递一个lvalue或者传递一个const lvaue  :

  • 传递一个lvalue,模板推导之后 T = std::string&
  • 传递一个const lvaue, 模板推导之后T = const std::string&
  • T& &&将折叠为T&,即std::string& && 折叠为 std::string&
  • 最终函数为: Foo(string& member): member{std::forward<string&>(member)} {}
  • std::forward<string&>(member)将返回一个左值,最终调用拷贝构造函数

2)传递一个rvalue:

  • 传递一个rvalue,模板推导之后 T = std::string
  • 最终函数为: Foo(string&& member): member{std::forward<string>(member)} {}
  • std::forward<string>(member) 将返回一个右值,最终调用移动构造函数

 

4、std::move()和std::forward()对比

(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<T>()不仅可以保持左值或者右值不变,同时还可以保持const、Lreference、Rreference、validate等属性不变。

例:

#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <memory>
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<class T1, class T2, class T3>
	B(T1 && t1, T2 && t2, T3 && t3) :
		a1_(std::forward<T1>(t1)),
		a2_(std::forward<T2>(t2)),
		a3_(std::forward<T3>(t3))
	{

	}
private:
	A a1_, a2_, a3_;
};

template <class T, class U>
std::unique_ptr<T> make_unique1(U&& u)
{
	//return std::unique_ptr<T>(new T(std::forward<U>(u)));
	return std::unique_ptr<T>(new T(std::move(u)));
}

template <class T, class... U>
std::unique_ptr<T> make_unique2(U&&... u)
{
	//return std::unique_ptr<T>(new T(std::forward<U>(u)...));
	return std::unique_ptr<T>(new T(std::move(u)...));
}

int main()
{
	auto p1 = make_unique1<A>(2);

	int i = 10;
	auto p2 = make_unique1<A>(i);

	int j = 100;
	auto p3 = make_unique2<B>(i, 2, j);

	system("pause");
	return 0;
}

forward时结果:

 

move时结果:

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值