右值引用和左值引用有什么区别?

What is the difference between rvalue reference and lvalue reference?

本文关键字:引用 区别 什么      更新时间:2023-10-16

在阅读了一些关于右值参考的材料后,我有更多的问题而不是答案。从这里我读到了关于右值参考:

  • 文档右值参考 (1(
  • 文档右值参考 (2(
  • 文档右值参考 (3 - 书(

在这里,我做了一个简单的例子来帮助我理解:

#include <iostream>
using namespace std;
class A
{
public:
A() :m_a(0), m_pa(nullptr) { cout << "constructor call" << endl; };
~A() { cout << "destructor call" << endl; };
A(A& other) :m_a(0), m_pa(nullptr)
{
cout << "copy constructor" << endl;
}
A(A&& other) :m_a(0), m_pa(nullptr)
{
cout << "move constructor" << endl;
}
A& operator=(A&& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
A& operator=(A& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
private:
int m_a;
int* m_pa;
};
int main()
{
A(test2);//constructor
A test4(test2);//copy constructor
//? - move constructor
return 0;
}

我不明白 && 有什么特别之处。在上面的例子中,我可以用 &

.
A& operator=(A& other)
{
this->m_a = other.m_a;  //copy value
this->m_pa = other.m_pa;//copy pointer address
other.m_a = 0;          
other.m_pa = nullptr;//clean "other" object properties from preventing destructor to delete them and lose pointer address
return *this;
}

问题:

  • 如果我可以在不使用额外内存分配和复制操作的情况下执行此操作,为什么我应该使用 &&?
  • 如何获取没有标识符的值并保存?

示例 2:

#include <iostream>
using namespace std;
void printReference (int& value)
{
cout << "lvalue: value = " << value << endl;
}
void printReference (int&& value)
{
cout << "rvalue: value = " << value << endl;
}
int getValue ()
{
int temp_ii = 99;
return temp_ii;
}
int main()
{ 
int ii = 11;
printReference(ii);
printReference(getValue());  //  printReference(99);
return 0;
}

问题:

  • 为什么在这种情况下使用 &&&,这对我有什么帮助?为什么不直接存储getValue的返回并打印出来呢?

在你读了一些关于右值的东西之后,这里有一些关于右值的更多材料。
我认为你可能错过的一点不是(不仅(你能做什么,而是你应该做什么。

您的第一个示例存在多个问题:

您无法将 const 值复制到A的实例。

const A a1;
A a2(a1);   // won't compile
A a3;
a3 = a1;    // won't compile

我不明白 && 有什么特别之处。在上面的例子中,我可以用 &

.

是的,你可以按照你的建议去做。但它纯粹是设计的副本同名。考虑一下:
我写了一个共享库,我的复制 ctor 就像你在建议中所做的一样。您无权访问我的代码,只能访问标头。在我的复制 ctor 和 assigment 运算符中,我拥有传递给我的库的实例的所有权。没有描述作业在做什么...你明白这一点吗,我绝不能拥有你的实例!例如:

// my header:
// copy ctor
A& operator=(A& other); 
// your code:
A a1;
A a2(a1);   // from now on, a1 is broken and you don't know it!!!
cout << a1; // pseudo code: prints garbage, UD, crash!!!

您始终应定义复制 ctors/赋值参数const

A(A const& other);
A& operator=(A const& other);
// ...
const A a1;
A a2(a1);           // will compile
A a3;
a3 = a1;            // will compile + due to the fact a1 is passed const a3 cannot mutate a1 (does not take ownership)
cout << a1;         // pseudo code: everything is fine
a3 = std::move(a1); // a3 takes ownership from a1 but you stated this explicitly as you want it

这里有一个小例子,你可以玩。请注意,复制构造函数是 const,但复制赋值不是。然后你可以看到它们的不同之处。

如果我可以在不使用额外内存分配和复制操作的情况下执行此操作,为什么我应该使用 &&?

你写的赋值运算符&左值引用是非常非常糟糕的。你不会期望这样的陈述

a = b;

损坏b,但这就是你的建议。通常的赋值运算符之所以采用const&正是因为它不应该改变表达式的右侧。

因此,当您确实想要窃取右侧的状态时,例如当它是匿名临时状态或您显式move它时,您应该使用 rvalue-reference 赋值运算符(移动赋值(:

a = return_anonymous_temporary(); // ok: the rhs value would expire anyway
a = std::move(b);                 // ok: the damage to b is explicit now

但是,这种行为不应该是默认设置。

如果我可以在不使用额外内存分配和复制操作的情况下执行此操作,为什么我应该使用 &&?

因为它允许您重载右值和左值的函数,并且每个函数都有不同的行为。您在两个重载中具有相同的行为,因此在这种情况下不需要右值引用。

更一般地说,右值引用参数允许您传递临时参数,同时允许从该参数移动。

为什么在这种情况下使用 &&&,这对我有什么帮助?

它允许您在参数为右值时打印"rvalue"

为什么不直接存储getValue的返回并打印出来呢?

然后,您将无法打印右值的"rvalue"和左值的"lvalue"


除了移动构造函数/赋值运算符之外,没有多少情况是值有用的。