1、移动语义是c++11引入,它的引入是为了解决对象赋值带来的开销,移动的实质是对象所有权的转移,它要比复制更为高效。
2、为了实现移动语义,编译器需要能够识别一个变量在什么时候只是个临时值,指没有名字的变量。
这里引入了左值、右值的概念(继承自C语言):
①左值:通常出现在赋值运算符的左侧(也可以是在右侧),它是在内存中有位置的对象,具有可访问性和可识别的内存地址。
②右值:通常出现在赋值运算符的右侧,它是一个临时对象或者子对象,右值也可以占用栈上的数据区的内存,但是这个内存是暂时分配的,而且赋值完后会马上释放,不可能给它赋值。
当一个对象是右值时,它的内容是可以转换为左值的:需要将右值保持为有效状态,这样它的析构函数才可以正常工作。
③右值引用:它使定位右值的内存位置成为了可能,你甚至可以定义指针指向这片内存。
3、使用移动语义的类,要为noexcept的,比如对于std::vector,它是强异常安全的:当一个vector执行某个操作时,如果发生异常,那么该vector的状态会与执行操作之前一样,复制构造函数并不会改变源对象,但是移动构造函数则会销毁它,任何在移动构造函数中发生的异常都会与强异常安全保证冲突。
4、std::move并不进行任何移动,std::forward也不进行任何转发,这俩函数在运行期什么都没做,它们是模板函数,是编译期行为,它们在编译期都仅仅是强制类型转换而已。
std::move是无条件的将实参强制转换成右值,而std::forward则在某个特定条件下才执行同一个强制类型转换。
可以在gcc源码中看到std::move、std::forward的实现:
gcc\libstdc++-v3\include\bits\move.h
/**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
/**
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
/**
* @brief Forward an rvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
给一个对象使用 std::move,就是告诉编译器该对象具备可移动的条件,这也是std::move得名的原因。
std::forward仅仅对绑定到右值的引用实施向右值类型的强制转换,一般万能引用作为参数。
5、T&& 有两种含义:
①右值引用
②既可以表示左值引用,也可以表示右值引用,二者选一,也即万能引用。
- 函数模板的形参:
template<typename T> void f(T&& param); //这里param就是个万能引用
-
auto声明的:
auto&& var2 = var1; //这里的var2就是万能引用
他们都涉及类型推导,所以万能引用也叫转发引用(forwarding references)。
因为是个引用,所以必需要初始化,万能引用的初始化值会决定他代表的是个左值还是右值引用。
要使一个引用成为万能引用,其涉及类型推导是必要条件,但还不是充分条件,引用申明的形式也必需正确无误,并且该形式被限定的很死,必需形如T&&才行,即上面列的两种形式。即使是一个const修饰,也会使其不能成为万能引用
这里涉及到引用折叠:含有左值引用时,结果就是左值;当两个都是右值时,结果才是右值引用。
6、针对右值引用使用std::move,针对万能引用使用std::forward。