C++ T&&;(双符号)以C+表示的平均值+;11?

C++ T&&;(双符号)以C+表示的平均值+;11?,c++,c++11,rvalue-reference,c++-faq,perfect-forwarding,C++,C++11,Rvalue Reference,C++ Faq,Perfect Forwarding,我一直在研究C++11的一些新特性,我注意到其中一个是在声明变量时使用了双符号,如T&&var 首先,这个野兽叫什么?我希望谷歌能允许我们搜索这样的标点符号 这到底是什么意思 乍一看,它似乎是一个双重引用(就像C风格的双指针T**var),但我很难想到它的用例。它声明了一个(标准提案文档) 下面是对右值的介绍 下面是微软标准库之一的右值引用的精彩深入介绍 注意:有关MSDN的链接文章(“VC10中的右值引用:C++0x功能,第2部分”)非常清楚地介绍了右值引用,但对右值引用做了一些陈述,这些陈述

我一直在研究C++11的一些新特性,我注意到其中一个是在声明变量时使用了双符号,如
T&&var

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

这到底是什么意思

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

它声明了一个(标准提案文档)

下面是对右值的介绍

下面是微软标准库之一的右值引用的精彩深入介绍

注意:有关MSDN的链接文章(“VC10中的右值引用:C++0x功能,第2部分”)非常清楚地介绍了右值引用,但对右值引用做了一些陈述,这些陈述在C++11标准草案中曾经是正确的,但在最后一个标准中却不是正确的!具体地说,它在不同的点上说,右值引用可以绑定到左值,这曾经是正确的,但是已经改变了(例如int x;int&&rrx=x;不再在GCC中编译)-drewbarbs 2014年7月13日16:12

C++03引用(现在在C++11中称为左值引用)之间的最大区别在于,它可以像临时值一样绑定到右值,而不必是常量。因此,此语法现在是合法的:

T&& r = T();
右值参考主要提供以下内容:

移动语义。现在可以定义一个移动构造函数和移动赋值运算符,它采用右值引用而不是通常的常量左值引用。移动的功能类似于复制,除非它没有义务保持源不变;事实上,它通常会修改源,使其不再拥有移动的资源。这对于消除无关拷贝非常有用,尤其是在标准库实现中

例如,复制构造函数可能如下所示:

foo(foo const& other)
{
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);
}
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
如果这个构造函数被传递了一个临时的,那么这个副本就没有必要了,因为我们知道临时的将被销毁;为什么不利用已经分配的临时资源呢?在C++03中,无法阻止复制,因为我们无法确定是否传递了临时副本。在C++11中,我们可以重载移动构造函数:

foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}
注意这里的巨大区别:move构造函数实际上修改了它的参数。这将有效地将临时文件“移动”到正在构造的对象中,从而消除不必要的副本

move构造函数将用于临时变量和使用
std::move
函数显式转换为右值引用的非常量左值引用(它只执行转换)。以下代码都调用了
f1
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"
完美转发。右值引用允许我们正确地转发模板函数的参数。以该工厂功能为例:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}
现在,当传递给
T
的构造函数时,参数的右值/左值性被保留。这意味着,如果使用右值调用factory,则使用右值调用
T
的构造函数。如果使用左值调用工厂,则使用左值调用
T
的构造函数。改进的factory功能之所以有效,是因为有一条特殊规则:

当函数参数类型为 表单
T&&
,其中
T
是一个模板 参数和函数参数 是类型
A
的左值,类型
A&
为 用于模板参数推断

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

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)
auto p1=factory(foo());//调用foo(foo&&)
自动p2=工厂(*p1);//调用foo(foo const&)
重要的右值参考属性

  • 对于重载解析,左值比左值引用更喜欢绑定,右值比右值引用更喜欢绑定。因此,为什么临时人员更喜欢调用移动构造函数/移动赋值操作符而不是复制构造函数/赋值操作符
  • 右值引用将隐式绑定到右值和隐式转换结果的临时值。i、 e.
    float f=0f;int&&i=f格式良好,因为float可以隐式转换为int;该引用将是转换结果的临时引用
  • 命名的右值引用是左值。未命名的右值引用是右值。这对于理解为什么在:
    foo&&r=foo()中需要调用
    std::move
    非常重要;foo f=std::move(r)

表示右值参考。除非以其他方式显式生成,否则右值引用将仅绑定到临时对象。它们用于在某些情况下使对象更加高效,并提供称为完美转发的功能,这大大简化了模板代码

在C++03中,无法区分不可变左值和右值的副本

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&);
在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&&);
考虑这些构造函数背后的实现。在第一种情况下,字符串必须执行复制以保留值语义,这涉及新的堆分配。然而,在第二种情况下,我们预先知道传递给构造函数的对象将立即被销毁,并且它不必保持不变。在这种情况下,我们可以有效地交换内部指针,而根本不执行任何复制,这大大提高了效率。移动语义使任何具有昂贵或禁止复制内部引用资源的类受益。考虑<代码> STD::UnQuyJPTR <代码> -现在我们的类可以区分临时和非临时性,我们可以使移动语义正确工作。
std::string s;
std::string& ref = s;
(std::string&)& anotherref = ref; // usually expressed via template
(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&&
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);
}
// template
template<class T> foo(T&& t) { ... }

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

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

// decltype
decltype(...)&& t = ...;
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&&)
 #include <cstring>
 class Sample {
  int *ptr; // large block of memory
  int size;
 public:
  Sample(int sz=0) : ptr{sz != 0 ? new int[sz] : nullptr}, size{sz} 
  {
     if (ptr != nullptr) memset(ptr, 0, sz);
  }
  // copy constructor that takes lvalue 
  Sample(const Sample& s) : ptr{s.size != 0 ? new int[s.size] :\
      nullptr}, size{s.size}
  {
     if (ptr != nullptr) memcpy(ptr, s.ptr, 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
      size = s.size;

      if (size != 0) {
        ptr = new int[s.size];
        memcpy(ptr, s.ptr, s.size);
      } else 
         ptr = nullptr;
     }
     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
};     
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, 
// first cast ptr from lvalue to rvalue
unique_ptr<int[]> new_owner = TakeOwnershipAndAlter(std::move(ptr),0);