What does T && (double ampersand) mean in C ++ 11?

I've been working on some new features of C++ 11, and one of the things I noticed is the use of the double ampersand when declaring variables such as T&var.

First of all, what is the name of this beast?I want Google to allow us to search for punctuation like this.

What does this mean?

At first glance, it may seem like a double reference (such as C-style double pointer T** var), but I can hardly take this into account.

#1st floor

The term T&&is often referred to as a forwarding reference when used in conjunction with type derivation, such as for perfect forwarding.The term "universal reference" is used by Scott Meyers Proposed in this article But was later changed.

That's because it can be either r or l.

For example:

// template
template<class T> foo(T&& t) { ... }

// auto
auto&& t = ...;

// typedef
typedef ... T;
T&& t = ...;

// decltype
decltype(...)&& t = ...;

More discussions can be found in the following answers: Syntax for universal references

#2nd floor

A right-value reference is an act similar to a normal reference X&with some exceptions.Most importantly, for function overload parsing, the left value prefers the old left value reference and the right value prefers the new right value reference:

void foo(X& x);  // lvalue reference overload
void foo(X&& x); // rvalue reference overload

X x;
X foobar();

foo(x);        // argument is lvalue: calls foo(X&)
foo(foobar()); // argument is rvalue: calls foo(X&&)

So what is the right value?Anything that is not a left value.The left value is an expression that represents a storage location, which allows us to get the address of that storage location through the &operator.

Start with an example to see what the right value can do:

 class Sample {
  int *ptr; // large block of memory
  int size;
  Sample(int sz=0) : ptr{sz != 0 ? new int[sz] : nullptr}, size{sz} 
  // copy constructor that takes lvalue 
  Sample(const Sample& s) : ptr{s.size != 0 ? new int[s.size] :\
      nullptr}, size{s.size}
     std::cout << "copy constructor called on lvalue\n";

  // move constructor that take rvalue
  Sample(Sample&& s) 
  {  // steal s's resources
     ptr = s.ptr;
     size = s.size;        
     s.ptr = nullptr; // destructive write
     s.size = 0;
     cout << "Move constructor called on rvalue." << std::endl;
  // normal copy assignment operator taking lvalue
  Sample& operator=(const Sample& s)
   if(this != &s) {
      delete [] ptr; // free current pointer
      ptr = new int[s.size]; 
      size = s.size; 
    cout << "Copy Assignment called on lvalue." << std::endl;
    return *this;
 // overloaded move assignment operator taking rvalue
 Sample& operator=(Sample&& lhs)
   if(this != &s) {
      delete [] ptr; //don't let ptr be orphaned 
      ptr = lhs.ptr;   //but now "steal" lhs, don't clone it.
      size = lhs.size; 
      lhs.ptr = nullptr; // lhs's new "stolen" state
      lhs.size = 0;
   cout << "Move Assignment called on rvalue" << std::endl;
   return *this;

Constructors and assignment operators are overloaded by versions with right-value references.Right-value references allow functions to call me on the condition "Do you want to call me on the left or right value at compile time (through overload resolution)?"Time Branch.This allows us to create more efficient constructors and assignment operators on top of them, moving resources instead of copying them.

The compiler automatically branches at compile time (depending on whether it is called for a left or right value), choosing whether the move constructor or the move assignment operator should be called.

Summary: Right-value references allow for mobile semantics (and perfect forwarding, discussed in the article links below).

An easy to understand practical example is the class template std:: unique_ptr.Because unique_ptr maintains exclusive ownership of its underlying original pointer, it cannot copy unique_ptr.This would violate its exclusive right to immutability.Therefore, they do not have a copy constructor.But they do have mobile constructors:

template<class T> class unique_ptr {
 unique_ptr(unique_ptr&& __u) noexcept; // move constructor

 std::unique_ptr<int[] pt1{new int[10]};  
 std::unique_ptr<int[]> ptr2{ptr1};// compile error: no copy ctor.  

 // So we must first cast ptr1 to an rvalue 
 std::unique_ptr<int[]> ptr2{std::move(ptr1)};  

std::unique_ptr<int[]> TakeOwnershipAndAlter(std::unique_ptr<int[]> param,\
 int size)      
  for (auto i = 0; i < size; ++i) {
     param[i] += 10;
  return param; // implicitly calls unique_ptr(unique_ptr&&)

// Now use function     
unique_ptr<int[]> ptr{new int[10]};

// first cast ptr from lvalue to rvalue
unique_ptr<int[]> new_owner = TakeOwnershipAndAlter(\
           static_cast<unique_ptr<int[]>&&>(ptr), 10);

cout << "output:\n";

for(auto i = 0; i< 10; ++i) {
   cout << new_owner[i] << ", ";

10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 

Static_cast<unique_ptr<int[]>&>(ptr) is usually std:: move to complete

// first cast ptr from lvalue to rvalue
unique_ptr<int[]> new_owner = TakeOwnershipAndAlter(std::move(ptr),0);

Thomas Becker's C ++ Rvalue References Explained It's a great article, including a lot of good examples, about all this, including how rvalue delivers perfect forwarding and what it means explain .This article relies mainly on his articles.

A brief introduction was written by Stroutrup et al. Introduction to Right Value References" .people

#3rd floor

It declares a Right Value Reference (Standard Recommendation Document).

This is the right value Introduction to References .

This is Microsoft Standard Library Developer One is an excellent in-depth study of rvalue references.

Note: The linked article on MSDN ("Rvalue Reference: C++ 0x functionality in VC10, Part 2") is a very clear introduction to Rvalue references, but the statement about Rvalue references was once correct in draft C++ 11.Standard, but not correct for the last one!Specifically, it says that right-value references can be bound to left-values at various points, which used to be true, but has been changed.(e.g. int x; int && rrx = x; no longer compiled in GCC) - drewbarbs July 13, 2014 at 16:12

The biggest difference between C ++ 03 references (now referred to as left-value references in C ++ 11) is that it binds to the right value like a temporary element, without using const.Therefore, this grammar is now legal:

T&& r = T();

Right-value references mainly provide the following:

Move semantics.You can now define a move constructor and a move assignment operator that uses a right-value reference instead of the usual const-left-value reference.Mobile functions like a replica, except it has no obligation to keep the source unchanged.In fact, it usually modifies the source so that it no longer owns the moved resource.This is useful for eliminating unrelated replicas, especially in standard library implementations.

For example, a copy constructor might look like this:

foo(foo const& other)
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);

If you pass this constructor to a temporary object, you do not need to copy because we know that the temporary object will be destroyed.Why not use the temporary resources that have been allocated?In C ++ 03, replication cannot be blocked because it is not possible to determine if we are being temporarily delivered.In C ++ 11, we can overload the move constructor:

foo(foo&& other)
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;

Notice the biggest difference here: the move constructor actually modifies its parameters.This will effectively "move" temporary files into the object being constructed, eliminating unnecessary copying.

The move constructor will be used for temporary object and non-constant left-value references, which are explicitly converted to right-value references using the std::move function (which performs conversion only).The following code calls the move constructor for f1 and f2:

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"

Perfect forwarding.Right-value references enable us to correctly forward the parameters of the template function.Take this factory function as an example:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
    return std::unique_ptr<T>(new T(a1));

If we call factory <foo> (5), the argument is deduced to be int&and will not be bound to text 5 even if the foo constructor uses int.Okay, we can use A1 const& instead, but what if foo accepts constructor parameters through non-const references?In order to achieve true universal factory functionality, we must overload our factories on A1&and A1 const&It might be good if factory takes one parameter type, but each other parameter type multiplies the required overload settings by two.This will soon become unmaintainable.

Right Value Reference solves this problem by allowing the standard library definition to properly forward the std::forward function for left/right value references.For more information on how std:: forward works, see here Excellent answers .

This enables us to define factory functions as follows:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));

Now, the rvalue / lvalue-ness of the parameter is preserved when passed to the constructor of T.This means that if factory is called with rvalue, T's constructor is called with rvalue.If factory is called with a left value, T's constructor is called with a left value.The improved factory functionality is effective because of a special rule:

Type A&is used for template parameter derivation when the form of function parameter type is T&&, where T is a template parameter and the function parameter is the left value of type A.

So we can use factories like this:

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

Important right-value reference properties:

  • For overload resolution, the left value tends to bind to the left value reference and the right value tends to bind to the right value reference.Therefore, why do temporaries prefer to call mobile constructors/mobile assignment operators over copy constructors/assignment operators?
  • Right value references implicitly bind to right values and temporary objects as the result of implicit conversion.That is, float f = 0f; int && I = f; float f = 0f; int && I = f; is well-formed because floats can be implicitly converted to int; the reference will be a temporary result of the conversion.
  • Named right value reference is left value.An unnamed right-value reference is a right-value.Understanding why STD must be made in the following places: move calls are important: foo && r = foo (); foo f = std::move (r); foo && r = foo (); foo f = std::move (r);

#4th floor

It represents the right value reference.Right-value references will only be bound to temporary objects unless otherwise explicitly generated.They are used to make objects more efficient in some cases and provide a feature called perfect forwarding, which greatly simplifies template code.

In C ++ 03, you cannot distinguish between copies of non-variable left and right values.

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(const std::string&);

This is not the case in C++ 0x.

std::string s;
std::string another(s);           // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(std::string&&);

Consider the implementation behind these constructors.In the first case, the string must be copied to preserve the value semantics, which involves a new heap allocation.However, in the second case, we know beforehand that the object passed to our constructor will be destroyed immediately, without having to remain the same.In this case, we can effectively exchange internal pointers without performing any replication at all, which is actually more efficient.Mobile semantics can benefit any class that is expensive or prohibits copying internal referenced resources.Consider the case of std::unique_ptr - Now our class can distinguish between temporary and non-temporary objects, we can make the move semantics work correctly so that unique_ptr cannot be copied but can be moved, which means that std::unique_ptr can be legally stored in a Standard container, sorted, and so on, while C++ 03 std::auto_ptr cannot.

Now, let's consider another use of right-value references - perfect forwarding.Consider binding references to references.

std::string s;
std::string& ref = s;
(std::string&)& anotherref = ref; // usually expressed via template

You can't remember what C++ 03 says, but in C++ 0x, the type of result you get when working with right-value references is critical.A reference to the right value of type T (where T is the reference type) becomes a reference to type T.

(std::string&)&& ref // ref is std::string&
(const std::string&)&& ref // ref is const std::string&
(std::string&&)&& ref // ref is std::string&&
(const std::string&&)&& ref // ref is const std::string&&

Consider the simplest template functionality - minimum and maximum.In C ++ 03, you must manually overload all four combinations of const and non-const.In C++ 0x, this is just an overload.Combined with variable parameter templates, perfect forwarding can be achieved.

template<typename A, typename B> auto min(A&& aref, B&& bref) {
    // for example, if you pass a const std::string& as first argument,
    // then A becomes const std::string& and by extension, aref becomes
    // const std::string&, completely maintaining it's type information.
    if (std::forward<A>(aref) < std::forward<B>(bref))
        return std::forward<A>(aref);
        return std::forward<B>(bref);

I omitted return type derivation because I don't remember how it was done, but min can accept any combination of left, right, and constant left values.

Keywords: Mobile Google

Added by Hamlets666 on Sun, 29 Dec 2019 07:49:50 +0200