From: http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/index.html? CA = dat-
Li Shengli, Senior Development Engineer, IBM
Introduction:C ++'s New Standard C ++ 11 has been released for some time. This article introduces a feature in the new standard, the right value reference and the transfer semantics. This feature makes the code more concise and efficient.
New Features
Rvalue referene is a new feature introduced in the new C ++ standard (C ++ 11, 11 represents 2011). It implements moving sementics) and precise transfer (perfect forwarding ). It has two main objectives:
- Eliminates unnecessary object copies during interaction between two objects, saves computing and storage resources, and improves efficiency.
- You can define generic functions more concisely and clearly.
Back to Top
Definition of left and right values
All expressions and variables in C ++ (including C) are either left or right. The common left value is a non-temporary object, which can be used in multiple statements. All variables meet this definition and can be used in multiple codes. They are left values. The right value refers to temporary objects, which are valid only in the current statement. See the following example:
- Simple assignment statement
In this statement, I is the left value, 0 is the temporary value, that is, the right value. In the following code, I can be referenced, and 0 won't. The immediate number is the right value.
- The right value can also appear on the left of the value assignment expression, but cannot be used as the value assignment object, because the right value is valid only in the current statement and the value assignment is meaningless.
For example:((i>0) ? i : j) = 1;
In this example, 0 appears on the left of "=" as the right value. However, if the value assignment object is I or J, the values are left.
Before C ++ 11, the right value cannot be referenced. To the maximum extent, bind a constant reference to the right value, for example:
In this case, the right value cannot be modified. However, the right value can be modified, for example:
T is a class. set is a function that assigns values to a variable in T. Get is used to retrieve the value of this variable. In this sentence, T () generates a temporary object, that is, the right value. Set () modifies the value of the variable and modifies the right value.
Since the right value can be modified, You can reference the right value. Right-value reference can easily solve problems in actual projects and achieve very attractive solutions.
Back to Top
Syntax symbols of the left and right values
The Declaration symbol of the Left value is "&". to distinguish it from the left value, the Declaration symbol of the right value is "&&".
Example program:
void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } void process_value(int&& i) { std::cout << "RValue processed: " << i << std::endl; } int main() { int a = 0; process_value(a); process_value(1); } |
Running result:
LValue processed: 0 RValue processed: 1 |
The process_value function is overloaded and accepts the left and right values respectively. The output result shows that the temporary object is processed as the right value.
However, if a temporary object is passed to another function through a function that accepts the right value, it becomes the left value, because this temporary object becomes a named object during the transfer process.
Example program:
void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } void process_value(int&& i) { std::cout << "RValue processed: " << i << std::endl; } void forward_value(int&& i) { process_value(i); } int main() { int a = 0; process_value(a); process_value(1); forward_value(2); } |
Running result:
LValue processed: 0 RValue processed: 1 LValue processed: 2 |
Although the number of immediate 2 values is the right value when the forward_value function receives the result, the value is changed to the left value when process_value is received.
Back to Top
Definition of transfer Semantics
The right value reference is used to support the transfer semantics. Transfer semantics transfers resources (heap, system objects, etc.) from one object to another, which can reduce the creation, copying, and destruction of unnecessary temporary objects, it can greatly improve the performance of C ++ applications. Temporary object maintenance (creation and destruction) has a serious impact on performance.
The transfer semantics is relative to the copy semantics. It can be analogous to the cut and copy of a file. When we copy a file from one directory to another, the speed is much slower than the cut.
Through the transfer semantics, resources in the temporary object can be transferred to other objects.
In the existing C ++ mechanism, we can define the copy constructor and the value assignment function. To implement the transfer semantics, you must define the transfer constructor and the transfer assignment operator. The transfer constructor and the transfer assignment operator are called to copy and assign values to the right value. If the transfer constructor and the transfer copy operator are not defined, the existing mechanism will be followed. The copy constructor and the value assignment operator will be called.
Common functions and operators can also use the right-value reference operator to implement the transfer semantics.
Back to Top
Implement transfer constructor and transfer assignment function
A simple string class is used as an example to implement the copy constructor and the copy assignment operator.
Example program:
class MyString { private: char* _data; size_t _len; void _init_data(const char *s) { _data = new char[_len+1]; memcpy(_data, s, _len); _data[_len] = '\0'; } public: MyString() { _data = NULL; _len = 0; } MyString(const char* p) { _len = strlen (p); _init_data(p); } MyString(const MyString& str) { _len = str._len; _init_data(str._data); std::cout << "Copy Constructor is called! source: " << str._data << std::endl; } MyString& operator=(const MyString& str) { if (this != &str) { _len = str._len; _init_data(str._data); } std::cout << "Copy Assignment is called! source: " << str._data << std::endl; return *this; } virtual ~MyString() { if (_data) free(_data); } }; int main() { MyString a; a = MyString("Hello"); std::vector<MyString> vec; vec.push_back(MyString("World")); } |
Running result:
Copy Assignment is called! source: Hello Copy Constructor is called! source: World |
This string class basically meets the needs of our demonstration. In the main function, the operation of calling the copy constructor and copying the value assignment operator is implemented. Both mystring ("hello") and mystring ("world") are temporary objects, that is, the right value. Although they are temporary, the program still calls the copy construction and copy assignment, resulting in meaningless resource application and release operations. If you can directly use the resources that the temporary object has applied for, it can save resources and save the time for resource application and release. This is the purpose of defining the transfer semantics.
First, we define the transfer constructor.
MyString(MyString&& str) { std::cout << "Move Constructor is called! source: " << str._data << std::endl; _len = str._len; _data = str._data; str._len = 0; str._data = NULL; } |
Similar to the copy constructor, pay attention to the following points:
1. the symbol of the parameter (Right Value) must be the right value reference symbol, that is, "&".
2. The parameter (Right Value) cannot be a constant because we need to modify the right value.
3. The resource link and tag of the parameter (Right Value) must be modified. Otherwise, the right destructor will release the resource. The resource transferred to the new object is invalid.
Now we define the transfer assignment operator.
MyString& operator=(MyString&& str) { std::cout << "Move Assignment is called! source: " << str._data << std::endl; if (this != &str) { _len = str._len; _data = str._data; str._len = 0; str._data = NULL; } return *this; } |
The problems that need attention here are the same as those of the transfer constructor.
After the transfer constructor and the transfer copy operator are added, the program runs as follows:
Move Assignment is called! source: Hello Move Constructor is called! source: World |
The compiler distinguishes the left and right values, and calls the transfer constructor and the transfer value assignment operator to the right value. Saves resources and increases the program running efficiency.
With the right value reference and transfer semantics, when designing and implementing classes, we should design the transfer constructor and transfer value assignment functions for classes that require dynamic application of a large number of resources, to improve application efficiency.
Back to Top
Standard library function STD: Move
Since the compiler can only call the transfer constructor and transfer value assignment functions for the right value reference, all named objects can only be left value references, if it is known that a named object is no longer used and you want to call the transfer constructor and transfer a value assignment function for it, that is, to use a left value reference as a right value reference, how can this problem be solved? The Standard Library provides the STD: Move function, which converts the left value reference to the right value reference in a very simple way.
Example program:
void ProcessValue(int& i) { std::cout << "LValue processed: " << i << std::endl; } void ProcessValue(int&& i) { std::cout << "RValue processed: " << i << std::endl; } int main() { int a = 0; ProcessValue(a); ProcessValue(std::move(a)); } |
Running result:
LValue processed: 0 RValue processed: 0 |
std::move
It is very helpful in improving the performance of swap functions. Generally,swap
The general definition of a function is as follows:
template <class T> swap(T& a, T& b) { T tmp(a); // copy a to tmp a = b; // copy b to a b = tmp; // copy tmp to b } |
With STD: Move, the swap function is defined:
template <class T> swap(T& a, T& b) { T tmp(std::move(a)); // move a to tmp a = std::move(b); // move b to a b = std::move(tmp); // move tmp to b } |
Using STD: Move, a simple swap function avoids three unnecessary copy operations.
Back to Top
Perfect forwarding)
This article uses precise transmission to express this meaning ." Perfect Forwarding is also translated into perfect forwarding and precise forwarding.
Accurate transfer is applicable to the scenario where a group of parameters must be passed to another function.
In C ++, apart from the parameter values, there are also two sets of attributes:
Left/right and const/non-Const. Precise transmission means that all these attributes and parameter values cannot be changed during parameter transmission. In generic functions, such requirements are very common.
The following is an example. The forward_value function is a generic function that passes a parameter to another function process_value.
Forward_value is defined:
template <typename T> void forward_value(const T& val) { process_value(val); } template <typename T> void forward_value(T& val) { process_value(val); } |
The forward_value function must overload two types of parameters, T & and const T &. Otherwise, the following four different types of parameters cannot be met simultaneously:
int a = 0; const int &b = 1; forward_value(a); // int& forward_value(b); // const int& forward_value(2); // int& |
For a parameter, it must be reloaded twice, that is, the number of function reloads is proportional to the number of parameters. The number of definitions of this function is very inefficient for programmers. Let's take a look at how the right value reference helps us solve this problem:
template <typename T> void forward_value(T&& val) { process_value(val); } |
You only need to define a parameter once and accept a parameter referenced by the right value to pass all the parameter types intact to the target function. All the four types of calls can be met. The left and right values of the parameters and the const/non-cosnt attributes are completely passed to the target function process_value. Isn't this solution concise and elegant?
int a = 0; const int &b = 1; forward_value(a); // int& forward_value(b); // const int& forward_value(2); // int&& |
The rule for deriving T & defined in C ++ 11 is:
The real parameter of the right value is the reference of the right value, and the real parameter of the Left value is still the reference of the Left value.
In a word, the parameter attributes remain unchanged. In this way, complete parameter transmission is perfectly realized.
On the surface, the right value reference only adds a reference symbol, but it has a great impact on the design of C ++ software and class libraries. It can simplify the code and improve the program running efficiency. Every C ++ software designer and programmer should understand and be able to apply it. When designing a class, we should also design the transfer constructor and transfer copy function if there are resources dynamically applied. When designing the class library, you should also consider the use cases of STD: Move and actively use it.
Back to Top
Summary
Right-value reference and transfer semantics are an important feature of the new C ++ standard. Every professional C ++ developer should master and apply it to actual projects. When there is a chance to refactor the code, you should also think about whether the application can be new. Before using it, check the compiler support.
References
Learning
- C ++ 11 standard new features: defaulted and deleted functions: This article introduces two new features of the C ++ 11 standard: defaulted and deleted functions, they are syntax extensions for the existing C ++ keywords default and delete, which can help developers easily control the default actions of the compiler, such as generating functions and conversions.
- See the C ++ 11 FAQ to learn about each feature.
- See
C ++ standard working draft.
- See C ++ 11 support in GCC to learn about the support of each feature in GCC.
- AIX and Unix zone: the "Aix and Unix zone" on developerworks provides a wealth of information related to all aspects of Aix system management that you can use to extend your UNIX skills.
- Getting started with AIX and Unix: visit the "getting started with AIX and Unix" page to learn more about AIX and UNIX.
- Summary of Aix and Unix topics: the AIX and Unix area has introduced many technical topics for you and summarized many popular knowledge points. We will continue to launch many related hot topics later. To facilitate your access, we will summarize all the topics in this area here, it makes it easier for you to find the content you need.
- AIX and Unix download center: here you can download IBM server software and tools that can run on AIX or Unix systems, so that you can try out their powerful functions for free in advance.
- IBM systems magazine for AIX Chinese version: This magazine focuses more on trends and enterprise-level architecture applications, at the same time, we also have in-depth discussions on emerging technologies, products, and application methods. IBM systems magazine is written by very senior industry insiders, including IBM partners, IBM host engineers, and senior management personnel. Therefore, you can understand the application concepts at a higher level, so that you can select and apply
IBM systems have a better understanding.