C++中表达式分为左值和右值,简单而言,有内存地址的表达式就是左值,它可以出现在赋值语句的左边或者右边。无法取内存地址的表达式是右值,只能出现在赋值语句的右边。 C++中常见的出现右值的情况:常量字面量、函数返回值或对象(左值引用类型除外)、匿名对象。右值引用可以实现移动语义与完美转发。
2. 移动语义移动构造函数和移动赋值运算符,统称为移动语义。C++11在标准库中引入了std::move函数,它的作用是“将表达式强行转为右值类型”。
#include3. 完美转发using namespace std; class CMyString { public: // 有参构造函数 CMyString(const char* pString) { m_iLen = strlen(pString) + 1; m_pBuffer = new char[m_iLen]; strcpy_s(m_pBuffer, m_iLen, pString); } // 拷贝构造函数 CMyString(CMyString& other) { m_iLen = other.m_iLen; m_pBuffer = new char[m_iLen]; strcpy_s(m_pBuffer, m_iLen, other.m_pBuffer); } // 拷贝赋值运算符 CMyString& operator=(const CMyString& other) { if (this != &other) { free(); m_iLen = other.m_iLen; m_pBuffer = new char[m_iLen]; strcpy_s(m_pBuffer, m_iLen, other.m_pBuffer); } return *this; } // 移动构造函数 CMyString(CMyString&& other) noexcept { m_iLen = other.m_iLen; m_pBuffer = other.m_pBuffer; other.m_pBuffer = nullptr; // 阻止该右值对象析构 } // 移动赋值运算符 CMyString& operator=(CMyString&& other) noexcept { if (this != &other) { free(); m_iLen = other.m_iLen; m_pBuffer = other.m_pBuffer; other.m_pBuffer = nullptr; } return *this; } // 析构函数 ~CMyString() { free(); } void free() { m_iLen = 0; if (m_pBuffer) { delete[] m_pBuffer; } } public: char* m_pBuffer; int m_iLen; }; int main() { CMyString str1("hello"); CMyString str2(move(str1)); // 移动构造函数 str2 = move(str1); // 移动赋值运算符 system("pause"); return 0; }
**有名字的右值引用其实是左值!**一个右值引用作为函数参数的形参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值,并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数,可以使用 C++11 提供的 std::forward () 函数,该函数实现的功能称之为完美转发。
// 函数原型 template万能引用T&& forward (typename remove_reference ::type& t) noexcept; template T&& forward (typename remove_reference ::type&& t) noexcept;
对于一个普通函数,它的形参,要么接受左值、要么接受右值类型。C++11 开始,规定了一种特殊的形式,函数形参既可以匹配左值,也可以匹配右值。这种情况必须是模板的形式,并且以&&作为形参数。
引用折叠为什么有万能引用?在 C++11 之前,是不允许引用的引用存在的。但是 C++11 之后,引用的引用在特定情况下允许存在,他们会在编译时被自动化简为左值引用或者右值引用,化简的过程称为引用折叠。
| 引用 | 引用折叠 |
|---|---|
| T& & | T& |
| T&& & | T& |
| T& && | T& |
| T&& && | T&& |
#includeusing namespace std; void fun1(int&) { cout << "fun1(int&)" << endl; } void fun2(int&&) { cout << "fun2(int&&)" << endl; } template void tfun(T&& arg) // 万能引用 { cout << "tfun(T&& arg)" << endl; } int main() { int value = 5; //fun1(5); // 错误 fun1(value); // 正确 fun2(5); // 正确 //fun2(value); // 错误 tfun(5); // 正确,T -> int, void tfun(int && arg),匹配右值。 tfun(value); // 正确,T -> int&,void tfun(int& && arg),引用折叠得到void tfun(int& arg),匹配左值。 system("pause"); return 0; }
// 实现完美转发 #includeusing namespace std; template void printValue(T& t) { cout << "left-value" << endl; } template void printValue(T&& t) { cout << "right-value" << endl; } template void testForward(T&& value) // 万能引用 { printValue(value); printValue(move(value)); printValue(forward (value)); cout << endl; } int main() { testForward(520); int num = 1314; testForward(num); testForward(forward (num)); testForward(forward (num)); testForward(forward (num)); system("pause"); return 0; }
参考:https://www.ttlarva.com/master/learn_cpp/01_rvalue_ref.html
参考:https://subingwen.cn/cpp/move-forward/



