C++11 std::move std::forward left and right value references moving constructors
- 2020-11-20 06:10:58
- OfStack
About C++11 new features of std::move, std::forward, left and right values reference there are a lot of online information, I mainly for the test performance of 1 test, to sort out the logic 1, first of all, the left value is more familiar, right value is a temporary variable, meaning that once used will not be used again. For these two types of values, l_value references and r_value references are introduced, as well as the concept of reference folding.
1. Sample test of rvalue reference
#include <iostream>
using namespace std;
// create 1 A test class
class A
{
public:
A() : m_a(55)
{
}
int m_a;
};
void funcA(A&& param) // Rvalues refer to parameters and accept only rvalues
{
cout << param.m_a << endl; // param with a The address of the 1 Send, just take 1 A new name
}
int main()
{
A a;
funcA(move(a)); // It must be converted to an rvalue
cout << a.m_a << endl; // Print normally, so std::move It doesn't have the ability to move
return 0;
}
2. Test examples of l_value and r_value references, and elicit universal references
Construct a set of overloaded functions that take rvalues, lvalues, and const A & Parameter overload function.
void funcA(const A& param)// Both rvalue references can be accepted , An lvalue reference can also be accepted , But there are 1 An implicit conversion const A&
void funcA(A& param)// Accept an lvalue reference
void funcA(A&& param) // Accept an rvalue reference
const A & param can accept both r_value and l_value references, but there is an implicit conversion and const usage is limited.
#include <iostream>
using namespace std;
// create 1 A test class
class A
{
public:
A() : m_a(55) // The constructor
{
cout << "Constructor" << endl;
}
A(const A & other) : m_a(55) // copy The constructor
{
cout << "Copy Constructor" << endl;
if (this == &other)
{
return;
}
this->m_a = other.m_a;
}
A& operator=(const A& other) // Assignment constructor
{
cout << "= Constructor" << endl;
if (this == &other)
{
return *this;
}
this->m_a = other.m_a;
return *this;
}
int m_a;
};
void test(A&& pa) // Test for rvalue
{
cout << " Only rvalues are accepted " << endl;
}
void funcA(const A& param) // Both rvalue references can be accepted , An lvalue reference can also be accepted , But there are 1 An implicit conversion const A&
{
//test(param); // Compile, however, param Rvalue is acceptable, but param Is converted to const The left value
//test(std::forward<A>(param)); // Compile, however, param Rvalue is acceptable, but param Is converted to const The left value
cout << param.m_a << endl;
}
void funcA(A& param) // Accept an lvalue reference
{
//test(param); // Compile, however, param Rvalue is acceptable, but param Is converted to the left value
test(std::forward<A>(param)); // Compile pass, pass forward forwarding
cout << param.m_a << endl;
}
void funcA(A&& param) // Accept an rvalue reference
{
//test(param); // Compile, however, param Is converted to the left value
test(std::forward<A>(param)); // Compile pass, pass forward forwarding
cout << param.m_a << endl;
}
int main()
{
A a;
const A& b = a;
funcA(a);
funcA(move(a));
funcA(b);
cout << a.m_a << endl; // Print normally, so std::move It doesn't have the ability to move
return 0;
}
For this C++11 introduces the concept of universal references, making it possible to accept both r_value and l_value references without so many overloaded functions. But inside the function, we need to call one more left-valued or right-valued function, we need the forward template class.
#include <iostream>
using namespace std;
// create 1 A test class
class A
{
public:
A() : m_a(new int(55)) // The constructor
{
cout << "Constructor" << endl;
}
A(const A & other) : m_a(new int(55)) // copy The constructor
{
cout << "Copy Constructor" << endl;
if (this == &other)
return;
this->m_a = other.m_a;
}
A& operator=(const A& other) // Assignment constructor
{
cout << "= Constructor" << endl;
if (this == &other)
return *this;
this->m_a = other.m_a;
return *this;
}
int* m_a;
};
void test(A&& pa) // Test for rvalue
{
cout << " Only rvalues are accepted " << endl;
}
void test(A& pa) // Tests for an lvalue
{
cout << " Only accept left values " << endl;
}
template<class T>
void funcA(T&& param)
{
test(std::forward<T>(param)); // Compile pass, pass forward Perfect forward
cout << *param.m_a << endl;
}
int main()
{
A a;
funcA(a);
funcA(move(a));
cout << *a.m_a << endl; // Print normally, so std::move It doesn't have the ability to move
return 0;
}
3. Derivation of the move constructor
All of the above features are the use of temporary variables, as much as possible to use temporary variables generated in the middle, to improve performance, so-called squeeze the final performance. Two things to note about the move constructor
1. The argument (to be moved) must be rvalue when the move constructor is called.
2. The movee can no longer be used after the move constructor is called.
#include <iostream>
using namespace std;
// create 1 A test class
class A
{
public:
A() : m_a(new int(55)) // The constructor
{
cout << "Constructor" << endl;
}
A(const A & other) : m_a(new int(55)) // copy The constructor
{
cout << "Copy Constructor" << endl;
if (this == &other)
{
return;
}
this->m_a = other.m_a;
}
A& operator=(const A& other) // Assignment constructor
{
cout << "= Constructor" << endl;
if (this == &other)
{
return *this;
}
this->m_a = other.m_a;
return *this;
}
A(A&& other) : m_a(other.m_a) // Move constructor, the argument is 1 A right value,
{
cout << "Move Constructor" << endl;
if (this == &other)
{
return;
}
other.m_a = nullptr; // Clears the moved object data after it has been moved
}
int* m_a;
};
void test(A&& pa) // Test for rvalue
{
cout << " Only rvalues are accepted " << endl;
}
void test(A& pa) // Tests for an lvalue
{
cout << " Only accept left values " << endl;
}
template<class T>
void funcA(T&& param)
{
test(std::forward<T>(param)); // Compile pass, pass forward Perfect forward
cout << *param.m_a << endl;
}
int main()
{
A a;
funcA(a);
funcA(move(a));
A b(move(a)); // Call the move constructor , The new object is b object
cout << *a.m_a << endl; // Data has been moved and the program crashed
return 0;
}
Move constructor 1 reduces temporary memory requests to a certain extent, reduces unnecessary copies, and saves space and time. The above features still need to be noted in the use of many places, if I encounter it will be added here in time, share with you, 1 start.