程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++11中rvalue references的使用

C++11中rvalue references的使用

編輯:關於C++

Rvalue references are a feature of C++ that was added with the C++11 standard. The syntax of an rvalue reference is to add && after a type.

In C++, there are rvalues and lvalues. An lvalue is an expression whose address can be taken,a locator value--essentially, an lvalue provides a (semi)permanent piece of memory. rvalues are not lvalues. An expression is an rvalue if it results in a temporary object.

Every C++ expression is either an lvalue or an rvalue. An lvalue refers to an object that persists beyond a single expression. You can think of an lvalue as an object that has a name. All variables, including nonmodifiable (const) variables, are lvalues. An rvalue is a temporary value that does not persist beyond the expression that uses it.

an "rvalue reference", that will let you bind a mutable reference to an rvalue, but not an lvalue. In other words, rvalue references are perfect for detecting if a value is temporary object or not. Rvalue references use the && syntax instead of just &, and can be const and non-const, just like lvalue references, although you'll rarely see a const rvalue reference.

Rvalue references solve at least two problems: Implementing move semantics; Perfect forwarding.

The original definition of lvalues and rvalues from the earliest days of C is as follows: An lvalue is an expression e that may appear on the left or on the right hand side of an assignment, whereas an rvalue is an expression that can only appear on the right hand side of an assignment.

If X is any type, then X&& is called an rvalue reference to X. For better distinction, the ordinary reference X& is now also called an lvalue reference.

rvalue references enable us to distinguish an lvalue from an rvalue.

In C++11,however, the rvalue reference lets us bind a mutable reference to an rvalue,but not an lvalue. In other words, rvalue references are perfect for detecting whether a value is a temporary object or not.

Important rvalue reference properties:

(1)、For overload resolution, lvalues prefer binding to lvalue references and rvalues prefer binding to rvalue references. Hence why temporaries prefer invoking a move constructor / move assignment operator over a copy constructor / assignment operator.

(2)、rvalue references will implicitly bind to rvalues and to temporaries that are the result of an implicit conversion. i.e. float f = 0f; int&& i = f; is well formed because float is implicitly convertible to int; the reference would be to a temporary that is the result of the conversion.

(3)、Named rvalue references are lvalues. Unnamed rvalue references are rvalues. This is important to understand why the std::move call is necessary in: foo&& r= foo(); foo f = std::move(r).

Rvalue references enable you to distinguish an lvalue from an rvalue. Lvalue references and rvalue references are syntactically and semantically similar,but they follow somewhat different rules.

右值引用是C++11中最重要的新特性之一,它解決了C++中大量的歷史遺留問題,使C++標准庫的實現在多種場景下消除了不必要的額外開銷(如std::vector, std::string),也使得另外一些標准庫(如std::unique_ptr, std::function)成為可能。即使你並不直接使用右值引用,也可以通過標准庫,間接從這一新特性中受益。

右值引用的意義通常解釋為兩大作用:移動語義和完美轉發。

右值引用可以使我們區分表達式的左值和右值。

右值引用它實現了移動語義(Move Sementics)和完美轉發(Perfect Forwarding)。它的主要目的有兩個方面:(1)、消除兩個對象交互時不必要的對象拷貝,節省運算存儲資源,提高效率;(2)、能夠更簡潔明確地定義泛型函數。

右值引用主要就是解決一個拷貝效率低下的問題,因為針對於右值,或者打算更改的左值,我們可以采用類似與auto_ptr的move(移動)操作,大大的提高性能(move semantics)。另外,C++的模板推斷機制為參數T&&做了一個例外規則,讓左值和右值的識別和轉向(forward)非常簡單,幫助我們寫出高效並且簡捷的泛型代碼(perfect forwarding)。

左值的聲明符號為”&”, 為了和左值區分,右值的聲明符號為”&&”。

下面是從其他文章中copy的測試代碼,詳細內容介紹可以參考對應的reference:

 

#include "rvalue_references.hpp"
#include 
#include 
#include 

//////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/language/reference
void double_string(std::string& s)
{
	s += s; // 's' is the same object as main()'s 'str'
}

char& char_number(std::string& s, std::size_t n)
{
	return s.at(n); // string::at() returns a reference to char
}

int test_lvalue_references1()
{
	// 1. Lvalue references can be used to alias an existing object (optionally with different cv-qualification):
	std::string s = "Ex";
	std::string& r1 = s;
	const std::string& r2 = s;

	r1 += "ample";           // modifies s
	//  r2 += "!";               // error: cannot modify through reference to const
	std::cout << r2 << '\n'; // prints s, which now holds "Example"

	// 2. They can also be used to implement pass-by-reference semantics in function calls:
	std::string str = "Test";
	double_string(str);
	std::cout << str << '\n';

	// 3. When a function's return type is lvalue reference, the function call expression becomes an lvalue expression
	std::string str_ = "Test";
	char_number(str_, 1) = 'a'; // the function call is lvalue, can be assigned to
	std::cout << str_ << '\n';

	return 0;
}

//////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/language/reference
static void f(int& x)
{
	std::cout << "lvalue reference overload f(" << x << ")\n";
}

static void f(const int& x)
{
	std::cout << "lvalue reference to const overload f(" << x << ")\n";
}

static void f(int&& x)
{
	std::cout << "rvalue reference overload f(" << x << ")\n";
}

int test_rvalue_references1()
{
	// 1. Rvalue references can be used to extend the lifetimes of temporary objects
	// (note, lvalue references to const can extend the lifetimes of temporary objects too, but they are not modifiable through them):
	std::string s1 = "Test";
	//  std::string&& r1 = s1;           // error: can't bind to lvalue

	const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime
	//  r2 += "Test";                    // error: can't modify through reference to const

	std::string&& r3 = s1 + s1;      // okay: rvalue reference extends lifetime
	r3 += "Test";                    // okay: can modify through reference to non-const
	std::cout << r3 << '\n';

	// 2. More importantly, when a function has both rvalue reference and lvalue reference overloads,
	// the rvalue reference overload binds to rvalues (including both prvalues and xvalues),
	// while the lvalue reference overload binds to lvalues:
	int i = 1;
	const int ci = 2;
	f(i);  // calls f(int&)
	f(ci); // calls f(const int&)
	f(3);  // calls f(int&&)
	// would call f(const int&) if f(int&&) overload wasn't provided
	f(std::move(i)); // calls f(int&&)

	// This allows move constructors, move assignment operators, and other move-aware functions
	// (e.g. vector::push_back() to be automatically selected when suitable.

	return 0;
}

/////////////////////////////////////////////////////
// reference: http://www.bogotobogo.com/cplusplus/C11/5_C11_Move_Semantics_Rvalue_Reference.php
static void printReference(int& value)
{
	std::cout << "lvalue: value = " << value << std::endl;
}

static void printReference(int&& value)
{
	std::cout << "rvalue: value = " << value << std::endl;
}

static int getValue()
{
	int temp_ii = 99;
	return temp_ii;
}

int test_rvalue_references2()
{
	int ii = 11;
	printReference(ii);
	printReference(getValue());  //  printReference(99);
	return 0;
}

////////////////////////////////////////////////////////////
// references: https://msdn.microsoft.com/en-us/library/dd293668.aspx
template struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.
template struct S {
	static void print(T& t)
	{
		std::cout << "print: " << t << std::endl;
	}
};

template struct S {
	static void print(const T& t)
	{
		std::cout << "print: " << t << std::endl;
	}
};

template struct S {
	static void print(T&& t)
	{
		std::cout << "print: " << t << std::endl;
	}
};

template struct S {
	static void print(const T&& t)
	{
		std::cout << "print: " << t << std::endl;
	}
};

// This function forwards its parameter to a specialized
// version of the S type.
template  void print_type_and_value(T&& t)
{
	S::print(std::forward(t));
}

// This function returns the constant string "fourth".
const std::string fourth() { return std::string("fourth"); }

int test_rvalue_references3()
{
	// The following call resolves to:
	// print_type_and_value(string& && t)
	// Which collapses to:
	// print_type_and_value(string& t)
	std::string s1("first");
	print_type_and_value(s1);

	// The following call resolves to:
	// print_type_and_value(const string& && t)
	// Which collapses to:
	// print_type_and_value(const string& t)
	const std::string s2("second");
	print_type_and_value(s2);

	// The following call resolves to:
	// print_type_and_value(string&& t)
	print_type_and_value(std::string("third"));

	// The following call resolves to:
	// print_type_and_value(const string&& t)
	print_type_and_value(fourth());

	return 0;
}

GitHub:https://github.com/fengbingchun/Messy_Test
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved