在c++ 11中T&& (double &&)是什么意思?

时间:2021-03-17 21:05:20

I've been looking into some of the new features of C++11 and one I've noticed is the double ampersand in declaring variables, like T&& var.

我一直在研究c++ 11的一些新特性,我注意到其中一个特性是在声明变量时使用双&& & var。

For a start, what is this beast called? I wish Google would allow us to search for punctuation like this.

首先,这只野兽叫什么?我希望谷歌允许我们搜索这样的标点符号。

What exactly does it mean?

这到底是什么意思?

At first glance, it appears to be a double reference (like the C-style double pointers T** var), but I'm having a hard time thinking of a use case for that.

乍一看,它似乎是一个双引用(就像c风格的双指针T** * var),但是我很难想到一个用例。

4 个解决方案

#1


535  

It declares an rvalue reference (standards proposal doc).

它声明一个rvalue引用(标准建议文档)。

Here's an introduction to rvalue references.

下面是rvalue引用的介绍。

Here's a fantastic in-depth look at rvalue references by one of Microsoft's standard library developers. (But see the Caution in the comments following this answer before reading this article.)

下面是微软标准库开发人员对rvalue引用的深入研究。(但在阅读本文之前,请参阅本文后面的评论。)

The biggest difference between a C++03 reference (now called an lvalue reference in C++11) is that it can bind to an rvalue like a temporary without having to be const. Thus, this syntax is now legal:

c++ 03引用(现在在c++ 11中称为lvalue引用)最大的不同之处在于,它可以像临时引用一样绑定到一个rvalue,而不必是const。因此,这种语法现在是合法的:

T&& r = T();

rvalue references primarily provide for the following:

rvalue引用主要提供以下内容:

Move semantics. A move constructor and move assignment operator can now be defined that takes an rvalue reference instead of the usual const-lvalue reference. A move functions like a copy, except it is not obliged to keep the source unchanged; in fact, it usually modifies the source such that it no longer owns the moved resources. This is great for eliminating extraneous copies, especially in standard library implementations.

移动语义。现在可以定义一个move构造函数和move赋值操作符,它接受一个rvalue引用,而不是通常的const-lvalue引用。移动功能类似于复制,但它没有义务保持源不变;实际上,它通常修改源文件,使其不再拥有移动的资源。这对于消除多余的副本非常有用,特别是在标准库实现中。

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 this constructor was passed a temporary, the copy would be unnecessary because we know the temporary will just be destroyed; why not make use of the resources the temporary already allocated? In C++03, there's no way to prevent the copy as we cannot determine we were passed a temporary. In C++11, we can overload a move constructor:

如果这个构造函数被传递为一个临时的,那么拷贝将是不必要的,因为我们知道临时的会被销毁;为什么不利用已经分配的临时资源呢?在c++ 03中,没有办法阻止拷贝,因为我们不能确定我们是临时通过的。在c++ 11中,我们可以重载一个move构造函数:

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

Notice the big difference here: the move constructor actually modifies its argument. This would effectively "move" the temporary into the object being constructed, thereby eliminating the unnecessary copy.

注意这里的巨大差异:move构造函数实际上修改了它的参数。这将有效地“移动”临时对象到正在构造的对象中,从而消除不必要的拷贝。

The move constructor would be used for temporaries and for non-const lvalue references that are explicitly converted to rvalue references using the std::move function (it just performs the conversion). The following code both invoke the move constructor for f1 and f2:

move构造函数将用于临时引用和使用std::move函数显式转换为rvalue引用的非const lvalue引用(它只是执行转换)。下面的代码都调用了f1和f2的move构造函数:

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. rvalue references allow us to properly forward arguments for templated functions. Take for example this factory function:

完美的转发。rvalue引用允许我们为模板化函数正确地转发参数。以工厂功能为例:

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

If we called factory<foo>(5), the argument will be deduced to be int&, which will not bind to a literal 5, even if foo's constructor takes an int. Well, we could instead use A1 const&, but what if foo takes the constructor argument by non-const reference? To make a truly generic factory function, we would have to overload factory on A1& and on A1 const&. That might be fine if factory takes 1 parameter type, but each additional parameter type would multiply the necessary overload set by 2. That's very quickly unmaintainable.

如果我们调用factory (5),这个参数将被推断为int&,它不会绑定到一个文字5,即使foo的构造函数取一个int。要做一个真正通用的工厂功能,我们必须在A1&和A1 const&i上超载工厂。如果factory采用1个参数类型,这可能很好,但是每个附加的参数类型将把必要的重载设置乘以2。这是很快的难题了。

rvalue references fix this problem by allowing the standard library to define a std::forward function that can properly forward lvalue/rvalue references. For more information about how std::forward works, see this excellent answer.

rvalue引用通过允许标准库定义std::forward函数来解决这个问题,该函数可以正确地转发lvalue/rvalue引用。有关std::forward如何工作的更多信息,请参见这个优秀的答案。

This enables us to define the factory function like this:

这使我们能够这样定义工厂功能:

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 argument's rvalue/lvalue-ness is preserved when passed to T's constructor. That means that if factory is called with an rvalue, T's constructor is called with an rvalue. If factory is called with an lvalue, T's constructor is called with an lvalue. The improved factory function works because of one special rule:

现在,当传递给T的构造函数时,参数的rvalue/lvalue性将被保留。这意味着如果用rvalue调用factory,则用rvalue调用T的构造函数。如果用lvalue调用factory,则用lvalue调用T的构造函数。改进后的工厂功能适用于一个特殊的规则:

When the function parameter type is of the form T&& where T is a template parameter, and the function argument is an lvalue of type A, the type A& is used for template argument deduction.

当函数参数类型为形式T&&,其中T为模板参数,函数参数为类型a的lvalue时,类型A&用于模板参数演绎。

Thus, we can use factory like so:

因此,我们可以这样使用工厂:

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

Important rvalue reference properties:

重要的右值引用属性:

  • 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.
  • 对于重载解析,lvalue更喜欢绑定而不是lvalue引用,而rvalue更喜欢绑定而不是rvalue引用。因此,为什么临时人员更喜欢调用move构造函数/ move赋值操作符而不是复制构造函数/赋值操作符。
  • 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.
  • rvalue引用将隐式地绑定到rvalue和隐式转换的结果的临时值。即f = 0f;int i = f;很好形成,因为浮动是隐式可转换为int;引用将指向转换的结果的临时引用。
  • 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引用是lvalues。未命名的rvalue引用是rvalues。理解为什么std::move调用在:foo&r = foo()中是必需的,这一点很重要;foo f = std::移动(r);

#2


75  

It denotes an rvalue reference. Rvalue references will only bind to temporary objects, unless explicitly generated otherwise. They are used to make objects much more efficient under certain circumstances, and to provide a facility known as perfect forwarding, which greatly simplifies template code.

它表示一个rvalue引用。Rvalue引用将只绑定到临时对象,除非显式地生成其他对象。在某些情况下,它们被用来使对象更加高效,并提供一个称为“完美转发”的工具,极大地简化了模板代码。

In C++03, you can't distinguish between a copy of a non-mutable lvalue and an rvalue.

在c++ 03中,您无法区分非可变lvalue的副本和rvalue的副本。

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&);

In C++0x, this is not the case.

在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 has to perform a copy to retain value semantics, which involves a new heap allocation. However, in the second case, we know in advance that the object which was passed in to our constructor is immediately due for destruction, and it doesn't have to remain untouched. We can effectively just swap the internal pointers and not perform any copying at all in this scenario, which is substantially more efficient. Move semantics benefit any class which has expensive or prohibited copying of internally referenced resources. Consider the case of std::unique_ptr- now that our class can distinguish between temporaries and non-temporaries, we can make the move semantics work correctly so that the unique_ptr cannot be copied but can be moved, which means that std::unique_ptr can be legally stored in Standard containers, sorted, etc, whereas C++03's std::auto_ptr cannot.

考虑这些构造函数背后的实现。在第一种情况下,字符串必须执行复制来保留值语义,这涉及到新的堆分配。但是,在第二种情况下,我们提前知道,传入我们构造函数的对象将立即被销毁,并且它不需要保持原样。我们可以有效地交换内部指针,而不是在这个场景中执行任何复制,这大大提高了效率。移动语义可以使任何对内部引用的资源进行昂贵或禁止复制的类受益。考虑的情况下std::unique_ptr -现在我们班可以区分临时变量和non-temporaries,我们可以移动语义正确工作因此unique_ptr不能复制,但可以移动,这意味着std::unique_ptr可以合法地存储在标准容器,排序,等等,而c++ 03的std::auto_ptr不能。

Now we consider the other use of rvalue references- perfect forwarding. Consider the question of binding a reference to a reference.

现在我们考虑rvalue引用的另一个用途——完美转发。考虑将引用绑定到引用的问题。

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

Can't recall what C++03 says about this, but in C++0x, the resultant type when dealing with rvalue references is critical. An rvalue reference to a type T, where T is a reference type, becomes a reference of type T.

我想不起来c++ 03是怎么说的,但是在c++ 0x中,处理rvalue引用时的结果类型是关键的。对类型T的rvalue引用(其中T是引用类型)成为类型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 function- min and max. In C++03 you have to overload for all four combinations of const and non-const manually. In C++0x it's just one overload. Combined with variadic templates, this enables perfect forwarding.

考虑最简单的模板函数——最小值和最大值。在c++ 03中,您必须手动重载所有四种const和非const组合。在c++ 0x中,它只是一个重载。结合可变模板,这可以实现完美的转发。

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);
    else
        return std::forward<B>(bref);
}

I left off the return type deduction, because I can't recall how it's done offhand, but that min can accept any combination of lvalues, rvalues, const lvalues.

我省略了返回类型演绎,因为我不记得它是怎么做的,但是min可以接受任何lvalue、rvalue、const lvalues的组合。

#3


18  

The term for T&& when used with type deduction (such as for perfect forwarding) is known colloquially as a universal reference. This was coined by Scott Meyers in this article.

与类型演绎(如完美转发)一起使用的术语&&被通俗地称为通用引用。这是Scott Meyers在这篇文章中创造出来的。

That is because it may be either r-value or l-value.

这是因为它可能是r值或l值。

Examples are:

例子有:

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

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

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

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

Note that the standard itself has no notion of this, it is simply a way to discuss the (odd?) combination of reference collapsing rules, reference type deduction, and the && syntax.

请注意,标准本身没有这个概念,它只是讨论引用崩溃规则、引用类型演绎和&&语法(奇数?)组合的一种方式。

More discussion can be found in the answer for: Syntax for universal references

更多的讨论可以在答案中找到:通用引用的语法

#4


6  

An rvalue reference is a type that behaves much like the ordinary reference X&, with several exceptions. The most important one is that when it comes to function overload resolution, lvalues prefer old-style lvalue references, whereas rvalues prefer the new rvalue references:

rvalue引用是一种类似于普通引用X&的类型,有几个例外。最重要的是,当涉及到函数重载解析时,lvalue更喜欢老式的lvalue引用,而rvalue更喜欢新的rvalue引用:

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 an rvalue? Anything that is not an lvalue. An lvalue being an expression that refers to a memory location and allows us to take the address of that memory location via the & operator.

那么什么是rvalue呢?任何不是lvalue的东西。lvalue是一个表示内存位置的表达式,它允许我们通过&操作符获取该内存位置的地址。

It is almost easier to understand first what rvalues accomplish with an example:

更容易理解的是rvalues通过一个例子实现了什么:

 class Sample {
  int *ptr; // large block of memory
  int size;
 public:
  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;
 }
//...snip
};     

The constructor and assignment operators have been overloaded with versions that take rvalue references. Rvalue references allow a function to branch at compile time (via overload resolution) on the condition "Am I being called on an lvalue or an rvalue?". This allowed us to create more efficient constructor and assignment operators above that move resources rather copy them.

构造函数和赋值操作符已经被接受rvalue引用的版本重载。Rvalue引用允许函数在编译时(通过重载解析)进行分支,条件是“我被调用是lvalue还是Rvalue ?”这使我们能够创建更高效的构造函数和赋值操作符,从而移动资源而不是复制它们。

The compiler automatically branches at compile time (depending on the whether it is being invoked for an lvalue or an rvalue) choosing whether the move constructor or move assignment operator should be called.

编译器在编译时自动分支(取决于是否为lvalue或rvalue调用它),选择是否应该调用move构造函数或move赋值操作符。

Summing up: rvalue references allow move semantics (and perfect forwarding, discussed in the article link below).

总结:rvalue引用允许移动语义(和完美的转发,在下面的文章链接中讨论)。

One practical easy-to-understand example is the class template std::unique_ptr. Since a unique_ptr maintains exclusive ownership of its underlying raw pointer, unique_ptr's can't be copied. That would violate their invariant of exclusive ownership. So they do not have copy constructors. But they do have move constructors:

一个实用的易于理解的示例是类模板std: unique_ptr。由于unique_ptr维护其底层原始指针的独占所有权,所以不能复制unique_ptr。这就违背了它们的排他性所有权。所以它们没有复制构造函数。但他们也有搬运工:

template<class T> class unique_ptr {
  //...snip
 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] << ", ";
}

output:
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 

static_cast<unique_ptr<int[]>&&>(ptr) is usually done using std::move

通常使用std::move来完成。

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

An excellent article explaining all this and more (like how rvalues allow perfect forwarding and what that means) with lots of good examples is Thomas Becker's C++ Rvalue References Explained. This post relied heavily on his article.

托马斯•贝克尔(Thomas Becker)的c++ Rvalue reference解释了这一切,以及更多(比如rvalues如何允许完美转发以及这意味着什么),并给出了许多很好的例子。这篇文章很大程度上依赖于他的文章。

A shorter introduction is A Brief Introduction to Rvalue References by Stroutrup, et. al

一个简短的介绍是一个简短的介绍Rvalue引用斯特拉胡夫等

#1


535  

It declares an rvalue reference (standards proposal doc).

它声明一个rvalue引用(标准建议文档)。

Here's an introduction to rvalue references.

下面是rvalue引用的介绍。

Here's a fantastic in-depth look at rvalue references by one of Microsoft's standard library developers. (But see the Caution in the comments following this answer before reading this article.)

下面是微软标准库开发人员对rvalue引用的深入研究。(但在阅读本文之前,请参阅本文后面的评论。)

The biggest difference between a C++03 reference (now called an lvalue reference in C++11) is that it can bind to an rvalue like a temporary without having to be const. Thus, this syntax is now legal:

c++ 03引用(现在在c++ 11中称为lvalue引用)最大的不同之处在于,它可以像临时引用一样绑定到一个rvalue,而不必是const。因此,这种语法现在是合法的:

T&& r = T();

rvalue references primarily provide for the following:

rvalue引用主要提供以下内容:

Move semantics. A move constructor and move assignment operator can now be defined that takes an rvalue reference instead of the usual const-lvalue reference. A move functions like a copy, except it is not obliged to keep the source unchanged; in fact, it usually modifies the source such that it no longer owns the moved resources. This is great for eliminating extraneous copies, especially in standard library implementations.

移动语义。现在可以定义一个move构造函数和move赋值操作符,它接受一个rvalue引用,而不是通常的const-lvalue引用。移动功能类似于复制,但它没有义务保持源不变;实际上,它通常修改源文件,使其不再拥有移动的资源。这对于消除多余的副本非常有用,特别是在标准库实现中。

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 this constructor was passed a temporary, the copy would be unnecessary because we know the temporary will just be destroyed; why not make use of the resources the temporary already allocated? In C++03, there's no way to prevent the copy as we cannot determine we were passed a temporary. In C++11, we can overload a move constructor:

如果这个构造函数被传递为一个临时的,那么拷贝将是不必要的,因为我们知道临时的会被销毁;为什么不利用已经分配的临时资源呢?在c++ 03中,没有办法阻止拷贝,因为我们不能确定我们是临时通过的。在c++ 11中,我们可以重载一个move构造函数:

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

Notice the big difference here: the move constructor actually modifies its argument. This would effectively "move" the temporary into the object being constructed, thereby eliminating the unnecessary copy.

注意这里的巨大差异:move构造函数实际上修改了它的参数。这将有效地“移动”临时对象到正在构造的对象中,从而消除不必要的拷贝。

The move constructor would be used for temporaries and for non-const lvalue references that are explicitly converted to rvalue references using the std::move function (it just performs the conversion). The following code both invoke the move constructor for f1 and f2:

move构造函数将用于临时引用和使用std::move函数显式转换为rvalue引用的非const lvalue引用(它只是执行转换)。下面的代码都调用了f1和f2的move构造函数:

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. rvalue references allow us to properly forward arguments for templated functions. Take for example this factory function:

完美的转发。rvalue引用允许我们为模板化函数正确地转发参数。以工厂功能为例:

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

If we called factory<foo>(5), the argument will be deduced to be int&, which will not bind to a literal 5, even if foo's constructor takes an int. Well, we could instead use A1 const&, but what if foo takes the constructor argument by non-const reference? To make a truly generic factory function, we would have to overload factory on A1& and on A1 const&. That might be fine if factory takes 1 parameter type, but each additional parameter type would multiply the necessary overload set by 2. That's very quickly unmaintainable.

如果我们调用factory (5),这个参数将被推断为int&,它不会绑定到一个文字5,即使foo的构造函数取一个int。要做一个真正通用的工厂功能,我们必须在A1&和A1 const&i上超载工厂。如果factory采用1个参数类型,这可能很好,但是每个附加的参数类型将把必要的重载设置乘以2。这是很快的难题了。

rvalue references fix this problem by allowing the standard library to define a std::forward function that can properly forward lvalue/rvalue references. For more information about how std::forward works, see this excellent answer.

rvalue引用通过允许标准库定义std::forward函数来解决这个问题,该函数可以正确地转发lvalue/rvalue引用。有关std::forward如何工作的更多信息,请参见这个优秀的答案。

This enables us to define the factory function like this:

这使我们能够这样定义工厂功能:

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 argument's rvalue/lvalue-ness is preserved when passed to T's constructor. That means that if factory is called with an rvalue, T's constructor is called with an rvalue. If factory is called with an lvalue, T's constructor is called with an lvalue. The improved factory function works because of one special rule:

现在,当传递给T的构造函数时,参数的rvalue/lvalue性将被保留。这意味着如果用rvalue调用factory,则用rvalue调用T的构造函数。如果用lvalue调用factory,则用lvalue调用T的构造函数。改进后的工厂功能适用于一个特殊的规则:

When the function parameter type is of the form T&& where T is a template parameter, and the function argument is an lvalue of type A, the type A& is used for template argument deduction.

当函数参数类型为形式T&&,其中T为模板参数,函数参数为类型a的lvalue时,类型A&用于模板参数演绎。

Thus, we can use factory like so:

因此,我们可以这样使用工厂:

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

Important rvalue reference properties:

重要的右值引用属性:

  • 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.
  • 对于重载解析,lvalue更喜欢绑定而不是lvalue引用,而rvalue更喜欢绑定而不是rvalue引用。因此,为什么临时人员更喜欢调用move构造函数/ move赋值操作符而不是复制构造函数/赋值操作符。
  • 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.
  • rvalue引用将隐式地绑定到rvalue和隐式转换的结果的临时值。即f = 0f;int i = f;很好形成,因为浮动是隐式可转换为int;引用将指向转换的结果的临时引用。
  • 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引用是lvalues。未命名的rvalue引用是rvalues。理解为什么std::move调用在:foo&r = foo()中是必需的,这一点很重要;foo f = std::移动(r);

#2


75  

It denotes an rvalue reference. Rvalue references will only bind to temporary objects, unless explicitly generated otherwise. They are used to make objects much more efficient under certain circumstances, and to provide a facility known as perfect forwarding, which greatly simplifies template code.

它表示一个rvalue引用。Rvalue引用将只绑定到临时对象,除非显式地生成其他对象。在某些情况下,它们被用来使对象更加高效,并提供一个称为“完美转发”的工具,极大地简化了模板代码。

In C++03, you can't distinguish between a copy of a non-mutable lvalue and an rvalue.

在c++ 03中,您无法区分非可变lvalue的副本和rvalue的副本。

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&);

In C++0x, this is not the case.

在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 has to perform a copy to retain value semantics, which involves a new heap allocation. However, in the second case, we know in advance that the object which was passed in to our constructor is immediately due for destruction, and it doesn't have to remain untouched. We can effectively just swap the internal pointers and not perform any copying at all in this scenario, which is substantially more efficient. Move semantics benefit any class which has expensive or prohibited copying of internally referenced resources. Consider the case of std::unique_ptr- now that our class can distinguish between temporaries and non-temporaries, we can make the move semantics work correctly so that the unique_ptr cannot be copied but can be moved, which means that std::unique_ptr can be legally stored in Standard containers, sorted, etc, whereas C++03's std::auto_ptr cannot.

考虑这些构造函数背后的实现。在第一种情况下,字符串必须执行复制来保留值语义,这涉及到新的堆分配。但是,在第二种情况下,我们提前知道,传入我们构造函数的对象将立即被销毁,并且它不需要保持原样。我们可以有效地交换内部指针,而不是在这个场景中执行任何复制,这大大提高了效率。移动语义可以使任何对内部引用的资源进行昂贵或禁止复制的类受益。考虑的情况下std::unique_ptr -现在我们班可以区分临时变量和non-temporaries,我们可以移动语义正确工作因此unique_ptr不能复制,但可以移动,这意味着std::unique_ptr可以合法地存储在标准容器,排序,等等,而c++ 03的std::auto_ptr不能。

Now we consider the other use of rvalue references- perfect forwarding. Consider the question of binding a reference to a reference.

现在我们考虑rvalue引用的另一个用途——完美转发。考虑将引用绑定到引用的问题。

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

Can't recall what C++03 says about this, but in C++0x, the resultant type when dealing with rvalue references is critical. An rvalue reference to a type T, where T is a reference type, becomes a reference of type T.

我想不起来c++ 03是怎么说的,但是在c++ 0x中,处理rvalue引用时的结果类型是关键的。对类型T的rvalue引用(其中T是引用类型)成为类型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 function- min and max. In C++03 you have to overload for all four combinations of const and non-const manually. In C++0x it's just one overload. Combined with variadic templates, this enables perfect forwarding.

考虑最简单的模板函数——最小值和最大值。在c++ 03中,您必须手动重载所有四种const和非const组合。在c++ 0x中,它只是一个重载。结合可变模板,这可以实现完美的转发。

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);
    else
        return std::forward<B>(bref);
}

I left off the return type deduction, because I can't recall how it's done offhand, but that min can accept any combination of lvalues, rvalues, const lvalues.

我省略了返回类型演绎,因为我不记得它是怎么做的,但是min可以接受任何lvalue、rvalue、const lvalues的组合。

#3


18  

The term for T&& when used with type deduction (such as for perfect forwarding) is known colloquially as a universal reference. This was coined by Scott Meyers in this article.

与类型演绎(如完美转发)一起使用的术语&&被通俗地称为通用引用。这是Scott Meyers在这篇文章中创造出来的。

That is because it may be either r-value or l-value.

这是因为它可能是r值或l值。

Examples are:

例子有:

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

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

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

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

Note that the standard itself has no notion of this, it is simply a way to discuss the (odd?) combination of reference collapsing rules, reference type deduction, and the && syntax.

请注意,标准本身没有这个概念,它只是讨论引用崩溃规则、引用类型演绎和&&语法(奇数?)组合的一种方式。

More discussion can be found in the answer for: Syntax for universal references

更多的讨论可以在答案中找到:通用引用的语法

#4


6  

An rvalue reference is a type that behaves much like the ordinary reference X&, with several exceptions. The most important one is that when it comes to function overload resolution, lvalues prefer old-style lvalue references, whereas rvalues prefer the new rvalue references:

rvalue引用是一种类似于普通引用X&的类型,有几个例外。最重要的是,当涉及到函数重载解析时,lvalue更喜欢老式的lvalue引用,而rvalue更喜欢新的rvalue引用:

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 an rvalue? Anything that is not an lvalue. An lvalue being an expression that refers to a memory location and allows us to take the address of that memory location via the & operator.

那么什么是rvalue呢?任何不是lvalue的东西。lvalue是一个表示内存位置的表达式,它允许我们通过&操作符获取该内存位置的地址。

It is almost easier to understand first what rvalues accomplish with an example:

更容易理解的是rvalues通过一个例子实现了什么:

 class Sample {
  int *ptr; // large block of memory
  int size;
 public:
  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;
 }
//...snip
};     

The constructor and assignment operators have been overloaded with versions that take rvalue references. Rvalue references allow a function to branch at compile time (via overload resolution) on the condition "Am I being called on an lvalue or an rvalue?". This allowed us to create more efficient constructor and assignment operators above that move resources rather copy them.

构造函数和赋值操作符已经被接受rvalue引用的版本重载。Rvalue引用允许函数在编译时(通过重载解析)进行分支,条件是“我被调用是lvalue还是Rvalue ?”这使我们能够创建更高效的构造函数和赋值操作符,从而移动资源而不是复制它们。

The compiler automatically branches at compile time (depending on the whether it is being invoked for an lvalue or an rvalue) choosing whether the move constructor or move assignment operator should be called.

编译器在编译时自动分支(取决于是否为lvalue或rvalue调用它),选择是否应该调用move构造函数或move赋值操作符。

Summing up: rvalue references allow move semantics (and perfect forwarding, discussed in the article link below).

总结:rvalue引用允许移动语义(和完美的转发,在下面的文章链接中讨论)。

One practical easy-to-understand example is the class template std::unique_ptr. Since a unique_ptr maintains exclusive ownership of its underlying raw pointer, unique_ptr's can't be copied. That would violate their invariant of exclusive ownership. So they do not have copy constructors. But they do have move constructors:

一个实用的易于理解的示例是类模板std: unique_ptr。由于unique_ptr维护其底层原始指针的独占所有权,所以不能复制unique_ptr。这就违背了它们的排他性所有权。所以它们没有复制构造函数。但他们也有搬运工:

template<class T> class unique_ptr {
  //...snip
 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] << ", ";
}

output:
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 

static_cast<unique_ptr<int[]>&&>(ptr) is usually done using std::move

通常使用std::move来完成。

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

An excellent article explaining all this and more (like how rvalues allow perfect forwarding and what that means) with lots of good examples is Thomas Becker's C++ Rvalue References Explained. This post relied heavily on his article.

托马斯•贝克尔(Thomas Becker)的c++ Rvalue reference解释了这一切,以及更多(比如rvalues如何允许完美转发以及这意味着什么),并给出了许多很好的例子。这篇文章很大程度上依赖于他的文章。

A shorter introduction is A Brief Introduction to Rvalue References by Stroutrup, et. al

一个简短的介绍是一个简短的介绍Rvalue引用斯特拉胡夫等