📜 ⬆️ ⬇️

Brief introduction to rvalue links

Translation of the article “A Brief Introduction to Rvalue References”, Howard E. Hinnant, Bjarne Stroustrup, Bronek Kozicki.

Rvalue links are a small technical extension of the C ++ language. They allow programmers to avoid logically unnecessary copying and to ensure perfect forwarding. First of all, they are intended for use in high-performance projects and libraries.

Introduction


This document gives a primary insight into the new function of the C ++ language - rvalue link. This is a quick tutorial, not a complete article. For more information, see the list of links at the end.
')

Rvalue link


A rvalue reference is a composite type, very similar to a traditional reference in C ++. To distinguish between these two types, we will call the traditional C ++ link an lvalue link. When the term link appears, it refers to both types of links, and to lvalue links, and to rvalue links.

According to the semantics of lvalue, a link is formed by putting & after some type.

A a; A& a_ref1 = a; //  lvalue  

If after some type put &&, then we get an rvalue link.

 A a; A&& a_ref2 = a; //  rvalue  

A rvalue reference behaves exactly the same as a lvalue reference, except that it can be associated with a temporary object, whereas an lvalue cannot be associated with a temporary (non-constant) object.

 A& a_ref3 = A(); // ! A&& a_ref4 = A(); // Ok 

Question: Why would we need it ?!

It turns out that the combination of rvalue of links and lvalue of links is what is needed for an easy implementation of move semantics. The Rvalue link can also be used to achieve perfect forwarding, which was previously an unsolved problem in C ++. For most programmers, the rvalue links allow you to create more productive libraries.

Move semantics


Elimination of side copies

Copying can be expensive. For example, for two vectors, when we write v2 = v1 , it usually causes a function call, memory allocation and a loop. This, of course, is acceptable when we really need two copies of a vector, but in many cases this is not so: we often copy a vector from one place to another, and then delete the old copy. Consider:

 template <class T> swap(T& a, T& b) { T tmp(a); //       a a = b; //        b b = tmp; //        tmp (.. a) } 

In reality, we didn’t need copies of a or b , we just wanted to exchange them. Let's try again:

 template <class T> swap(T& a, T& b) { T tmp(std::move(a)); a = std::move(b); b = std::move(tmp); } 

This call move() returns the value of the object passed as a parameter, but does not guarantee the safety of this object. For example, if we pass vector as a parameter in move() , then we can reasonably expect that after the function works, the parameter will have a zero-length vector, since all elements will be moved rather than copied. In other words, a move is an erase read (destructive read).

In this case, we optimized swap specialization. However, we cannot specialize every function that copies a large object just before it deletes or overwrites it. It would be non-constructive.

The main task of link rvalue is to allow us to implement the move without rewriting the code and runtime overhead.

Move

The move function actually performs a very modest job. Its task is to accept either the lvalue or the rvalue parameter, and return it as an rvalue without calling the copy constructor:

 template <class T> typename remove_reference<T>::type&& move(T&& a) { return a; } 

Now everything depends on the client code, where the key functions (for example, the copy constructor and the assignment operator) that determine whether the lvalue parameter or rvalue should be overloaded. If the lvalue parameter, then you need to copy. If rvalue, then you can safely perform the move.

Overload for lvalue / rvalue

Consider a simple class that owns a resource and also provides copy semantics (copy constructor and assignment operator). For example, clone_ptr could own a pointer and call its expensive clone() method to copy:

 template <class T> class clone_ptr { private: T* ptr; public: //  explicit clone_ptr(T* p = 0) : ptr(p) {} //  ~clone_ptr() {delete ptr;} //   clone_ptr(const clone_ptr& p) : ptr(p.ptr ? p.ptr->clone() : 0) {} clone_ptr& operator=(const clone_ptr& p) { if (this != &p) { delete ptr; ptr = p.ptr ? p.ptr->clone() : 0; } return *this; } //   clone_ptr(clone_ptr&& p) : ptr(p.ptr) {p.ptr = 0;} clone_ptr& operator=(clone_ptr&& p) { std::swap(ptr, p.ptr); return *this; } //   T& operator*() const {return *ptr;} // ... }; 

With the exception of move semantics, clone_ptr is the code that can be found in today's C ++ books. Users could use clone_ptr like this:

 clone_ptr<base> p1(new derived); // ... clone_ptr<base> p2 = p1; //  p2  p1      


Note that doing the copy constructor or assignment operator for clone_ptr is a relatively expensive operation. However, when the source of the copy is an rvalue, you can avoid calling the potentially expensive clone() operation by stealing the rvalue pointer (no one will notice!). In the semantics of relocation, the relocation constructor leaves the value of rvalue in the created object, and the assignment operator reverses the values ​​of the current object with the rvalue object of the link.

Now, when the code is trying to copy rvalue clone_ptr , or if there is explicit permission to read the source of the rvalue copy (using std::move ), the work will be done much faster.

 clone_ptr<base> p1(new derived); // ... clone_ptr<base> p2 = std::move(p1); //  p2  ,  p1 

For classes composed of other classes (either through inclusion or through inheritance), the move constructor and the move assignment can easily be implemented using the std::move function.

 class Derived : public Base { std::vector<int> vec; std::string name; // ... public: // ... //   Derived(Derived&& x) //   rvalue : Base(std::move(x)), vec(std::move(x.vec)), name(std::move(x.name)) { } Derived& operator=(Derived&& x) //   rvalue { Base::operator=(std::move(x)); vec = std::move(x.vec); name = std::move(x.name); return *this; } // ... }; 

Each sub-object will now be processed as an rvalue in the displacement constructor and the object's moving assignment operator. With std::vector and std::string , move operations are already implemented (just like our clone_ptr ), which allows you to avoid much more expensive copy operations.

It is worth noting that the x parameter is treated as an lvalue in move operations, despite the fact that it is declared as a rvalue reference. Therefore, you must use move(x) instead of just x when passing to the base class. This is a key security mechanism for semantics of motion, designed to prevent accidental double-move attempts from some named variable. All movements occur only from rvalues ​​or with an explicit reduction to rvalue (with std::move ). If the variable has a name, then it is an lvalue.

Question: What about types that do not own resources? (For example, std::complex ?)

In this case, no work is required. The copy constructor is already optimal for copying with rvalue.

Relocatable but not replicable types


For some types of semantics of copying is not applicable, but they can be moved. For example:


If such types are made relocatable (although they remain not replicable), then their usability is extremely increased. The moved but not copied object can be returned by value from the factory method (pattern):

 ifstream find_and_open_data_file(/* ... */); ... ifstream data_file = find_and_open_data_file(/* ... */); //  ! 

In this example, the base file descriptor is transferred from one object to another, because the ifstream source is rvalue. There is only one file descriptor at any given time, and only ifstream owns it.

A relocatable but non-replicable type can also be placed in standard containers. If the container needs to “copy” the element inside itself (for example, during vector relocation), it will simply move it instead of copying.

 vector<unique_ptr<base>> v1, v2; v1.push_back(unique_ptr<base>(new derived())); // OK,    ... v2 = v1; //   !    . v2 = move(v1); //  .     v2. 

Many standard algorithms benefit from moving sequence elements instead of copying them. This not only provides better performance (as in the case of std::swap , the implementation of which is described above), but also allows these algorithms to work with non-copied (but relocatable) types. For example, the following code sorts
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .

vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .

vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
 vector<unique_ptr>,   ,     : 

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .

vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .

vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .
vector<unique_ptr>, , :

struct indirect_less { template <class T> bool operator()(const T& x, const T& y) {return *x < *y;} }; ... std::vector<std::unique_ptr<A>> v; ... std::sort(v.begin(), v.end(), indirect_less());

unique_ptr , swap ( , ) / . , , . ( , ), .

(perfect forwarding)
, std::shared_ptr . . , , . :

template <class T> std::shared_ptr<T> factory() // { return std::shared_ptr<T>(new T); } template <class T, class A1> std::shared_ptr<T> factory(const A1& a1) // { return std::shared_ptr<T>(new T(a1)); } //
. :

std::shared_ptr<A> p = factory<A>(5);
: , T ?

, factory T .

factory :

template <class T, class A1> std::shared_ptr<T> factory(A1& a1) { return std::shared_ptr<T>(new T(a1)); }
. const factory , (, A1 ) T . , , T . (, std::bind ).

:

std::shared_ptr<A> p = factory<A>(5); // ! A* q = new A(5); // OK
factory , "5" factory , int& rvalue "5". , , .

: AI & const AI &?

, : 4 . 8 . 16 .. .

Rvalue :

template <class T, class A1> std::shared_ptr<T> factory(A1&& a1) { return std::shared_ptr<T>(new T(std::forward<A1>(a1))); }
rvalue factory . const , factory .

: forward ?

move , forward - , , , . a1 , .

, forward lvalue/rvalue , factory . factory rvalue, forward T rvalue. , lvalue factory , T lvalue.

forward :

template <class T> struct identity { typedef T type; }; template <class T> T&& forward(typename identity<T>::type&& a) { return a; }

, . , 95% .

, , , , N1377 .

N1385 .

rvalue ( ), N1690 .

, rvalue , N1952 .

rvalue , N2027 .

rvalue , N1771 .

, rvalue , :
N1856 N1857 N1858 N1859 N1860 N1861 N1862
rvalue (this), N1821 .

Source: https://habr.com/ru/post/226229/


All Articles