Detailed explanation of rvalue reference in C ++

References in 98

  1. concept
  2. characteristic
  3. Cited usage scenarios
  4. Comparison of the efficiency of the three parameter transmission methods
  5. Exploration: the underlying implementation of the reference -----> pointer
    • T&------>T* const
    • const T&---->const T*const
  6. The difference between references and pointers
Quoted summary

Rvalue reference in 11

Why have rvalue references

In order to improve the efficiency of program operation, rvalue reference was introduced in C ++ 11, rvalue reference is also an alias, but it can only refer to rvalue

int main()
{

	//右值引用---->引用形式----->只能引用右值
	int a = 10;
	int b = 20;

	//a可以为左值,能放到=号左侧的一定是左值
	//能放到=号右侧的不一定是右值
	a = b;

	//ra对10的右值引用,ra称为对10的别名
	int&& ra = 10;

	system("pause");
	return 0;
}

Lvalue and rvalue

It is generally believed that: those that can be placed on the left of =, or those that can take the address are called lvalues, those that can only be placed on the right of =, or those that cannot take the address are called rvalue , but there are definitely special cases, not necessarily the above Completely correct

int  main()
{
	const int a = 10;

	//a = 100; a不能出现在左侧
	//a也不是右值
	//a能够取地址
	cout << &a << endl;
	//int && ra = a;
	system("pause");
	return 0;
}

Summary of lvalue and rvalue

Lvalues ​​and rvalues ​​are not well distinguished, and it is generally believed that:

  1. Ordinary types of variables, because they have names, can take addresses, are considered lvalues
  2. const-modified constants, unmodifiable, read-only types, in theory should be treated as rvalues, but because they can take addresses (if only const type constants are defined, the compiler does not open up space for them, if you take the address of the constant When the compiler opened up space for it), C ++ 11 considered it to be an lvalue
  3. If the result of the expression is a temporary variable or object, it is considered to be an rvalue
  4. If the result of the expression or a single variable is a reference, it is considered an lvalue
int b = 10;//为右值
int&& rb = b + 10;
int g_a = 10;

int& GetG_A()
{
	return g_a;
}

int main()
{
	GetG_A() = 10;
	//下面这行代码编译报错
	//int && rc = GetG_A();
	return 0;
}

You can't judge the lvalue or rvalue simply by whether you can put it on the left side of the == left side or take the address. You need to judge according to the result of the expression or the nature of the variable, such as the above: c constant

Rvalue in C ++ 11

An rvalue reference, as the name suggests, is a reference to an rvalue. In C ++ 11, rvalues ​​consist of two concepts: pure rvalues ​​and dying values.

  • Pure rvalues
    Pure rvalues ​​are the concept of rvalues ​​in C ++ 98. They are used to identify temporary variables and some values ​​that are not associated with objects. For example: constants, some arithmetic expressions (1 + 3), etc.
  • Decimal value
    Declare the object whose period is about to end. For example: temporary objects when the value is returned.
//将亡值
//值得方式返回
//使用ret--->创建一个临时变量---->函数运行结束栈空间被回收
int Add(int a, int b)
{
	int ret = a + b;
	return ret;
}

int main()
{
	int&& Ret = Add(10,20);
	return 0;
}

Comparison of references and rvalue references

//C++98引用
//1. 普通类型得引用
//2. cosnt类型的引用
int main()
{
	//98中的普通类型的引用不能引用右值
	int a = 10;
	int&ra = a;
	//int&rra = 10;	不行

	//98中的const引用既可以引用左值也可以引用右值
	const int&cral = a;
	const int& cra2 = 10;
	return 0;
}

Rvalue reference in C ++ 11: only rvalue can be quoted, lvalue cannot be directly quoted in general

int main()
{
	//10纯右值,本来只是一个符号,没有具体空间
	//右值引用变量r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量
	int&& r1=10;
	r1=100;
	int a=10;
	int&& r2=a; //编译失败:右值引用不能引用左值
	return 0;
}

std::move()

In C ++ 11, the std :: move () function is located in the header file. The name of this function is confusing. It does not move anything. The only function is to force an lvalue to be converted to an rvalue reference. The reference uses this value to implement move semantics. Note: The converted value of the lvalue does not change with the conversion of the left and right values, that is, the lvalue variable converted by std :: move will not be destroyed .

int main()
{
	int a = 10;
	int&& ra = move(a);
	return 0;
}

Scenarios referenced by rvalues

class String
{
public:
	String(char* str = "")
	{
		if (nullptr == str)
			str = "";
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}

	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* pTemp = new char[strlen(s._str) + 1];
			strcpy(pTemp, s._str);
			delete[] _str;
			_str = pTemp;
		}
		return *this;
	}

	String operator+(const String&s)
	{
		char* pTemp = new char[strlen(_str) + strlen(s._str)];
		strcpy(pTemp, _str);
		strcpy(pTemp + strlen(_str), s._str);
		String strRet(pTemp);
		delete pTemp;
		return strRet;
	}

	~String()
	{
		if (_str) delete[] _str;
	}
private:
	char* _str;
};
int main()
{
	String s1("hello");
	String s2("world");
	String s3(s1 + s2);
	return 0;
}

Assume that the compiler does not optimize
the position of the return value. The above code creates a temporary variable when the plus operator is overloaded, and stores the concatenated string. When returning, it returns by value . A temporary object is copied through strRet . The strRet in the function body has been destroyed after returning. So the final copy of s3 is constructed by temporary objects, so s3 also needs to create a temporary variable . Once the copy is constructed, the temporary object is released.
One is just released and applied again, which is a bit superfluous. Can not let s3 create another temporary object
We can let s3 use the temporary object of the return value directly.

Mobile semantics

C ++ 11 proposed the concept of mobile semantics: the way to move resources in one object to another

  1. Can effectively relieve memory pressure
  2. Improve the operating efficiency of the program, because there is no need to open up space and free up space
//移动构造
//移动构造参数一定不能加cosnt,资源可以转移出去,但是资源不能清空了
String(String&& s)
	:_str(s._str)
{
	s._str = nullptr;
}
  1. Mobile constructor parameters cannot be added with rvalue references of type const, because resources cannot be transferred, resulting in semantic failure
  2. In C ++ 11, the compiler will generate a mobile structure for the class by default. The mobile structure is a shallow copy. Therefore, when resource management is involved in the class, the user must explicitly define his own mobile structure
    in C ++ 11, copy Construction / moving construction / assignment / moving assignment functions must be provided at the same time, or not provided at the same time, the program can ensure that the class has both copy and move semantics

Resource transfer in auto_ptr

	auto_ptr<int> sp1(new  int);
	auto_ptr<int> sp2(sp1);
	//sp1资源给了sp2,但是sp1生命周期还没有到头,所以不能被使用

Rvalue reference refers to lvalue

Use the move function to convert the lvalue to the rvalue.

class Person
{
public:
	Person(char* name, char* sex, int age)
		: _name(name)
		, _sex(sex)
		, _age(age)
	{}
	Person(const Person& p)
		: _name(p._name)
		, _sex(p._sex)
		, _age(p._age)
	{}
#if 0
	Person(Person&& p)
		: _name(p._name)
		, _sex(p._sex)
		, _age(p._age)
	{}
#else
	Person(Person&& p)
		: _name(move(p._name))
		, _sex(move(p._sex))
		, _age(p._age)
	{}
	//移动赋值
	Person& operator=(Person&&p)
	{
		return *this;
	}
#endif
private:
	String _name;
	String _sex;
	int _age;
};
Person GetTempPerson()
{
	Person pp("prety", "male", 18);
	return pp;		//pp确实是右值,但是里面的成员变量是当作左值,所以用move把左值转化为右值
}
int main()
{
	Person p(GetTempPerson());
	system("pause");
	return 0;
}

Use the timing of move to determine that the current variable is a perishable value, such as the function return value .

Perfect forwarding

Perfect forwarding means that in the function template, the parameters are passed to another function called in the function template exactly according to the type of the template's parameters

void Func(int x)
{
	// ......
}
template<typename T>
void PerfectForward(T t)
{
	Fun(t);
}

PerfectForward is the template function for forwarding, Func is the actual target function, but the above forwarding is not perfect **, perfect forwarding is that the target function always hopes to transfer the parameters to the target function according to the actual type passed to the forwarding function without generating additional Overhead **, Fun (t) in the above code is just a copy of the external t, as if the forwarder does not exist.

The so-called perfection:

When a function template passes its own formal parameters to other functions, if the corresponding actual parameter is an lvalue, it should be forwarded as an lvalue; if the corresponding actual parameter is an rvalue, it should be forwarded as an rvalue . This is done to preserve the different processing of the left and right value attributes of the forwarded parameters by other functions (such as copy semantics when the parameter is an lvalue; move semantics when the parameter is an rvalue).

C ++ 11 uses forward function to achieve perfect forwarding:

void Fun(int &x)	//普通类型引用
{ 
	cout << "lvalue ref" << endl; 
}
void Fun(int &&x)	//普通类型右值引用
{ 
	cout << "rvalue ref" << endl; 
}
void Fun(const int &x)	//const类型普通引用
{
	cout << "const lvalue ref" << endl; 
}
void Fun(const int &&x)	//const类型的右值引用
{ 
	cout << "const rvalue ref" << endl; 
}

//通过函数模板作为转发函数
template<typename T>
//完美转发:t是左值---->传给Fun函数,t应该也是左值
//		   t如果是右值--->传给Fun函数,t应该是右值
void PerfectForward(T &&t)
{ 
	Fun(std::forward<T>(t)); //forward把t转化为右值。
	//Fun(t);
}

int main()
{
	PerfectForward(10); // 10为右值
	int a;
	PerfectForward(a); // lvalue ref
	PerfectForward(std::move(a)); // rvalue ref
	const int b = 8;
	PerfectForward(b); // const lvalue ref
	PerfectForward(std::move(b)); // const rvalue ref
	return 0;
}

foward和move

  • move to achieve move semantics
  • forward achieves perfect forwarding

Rvalue reference function

  1. Implement mobile semantics
  2. Alias ​​intermediate temporary variables
  3. Achieve perfect forwarding
  4. rvalue references are used for both construction and insertion in unordered_map. rC emplacereference is also used for insertion (tail insertion) in vectorC ++ 11 .

pash_back; the object must be constructed well
emplace: construction plus tail insertion

Published 253 original articles · praised 41 · 40,000+ views

Guess you like

Origin blog.csdn.net/liuyuchen282828/article/details/103967143