什么是T& (双和符号)是什么意思在C ++ 11? [英] What does T&& (double ampersand) mean in C++11?

查看:207
本文介绍了什么是T& (双和符号)是什么意思在C ++ 11?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究C ++ 11的一些新功能,一个我注意到的是声明变量的双重符号,例如 T&& var



一开始,这个野兽叫什么?我希望Google允许我们搜索这样的标点符号。



这是什么意思?


$乍一看,它似乎是一个双引用(像C风格的双指针 T ** var ),但我有一个。

解决方案

它声明一个价值参考(标准建议文件)。



这里参考的简介。



这是一个奇妙的深入研究微软的标准库中的一个微软引用开发人员。 (但阅读本文之前,请参阅本回答之后的注释中的注意事项。)



C ++ 03引用(现在称为C中的左值引用++ 0x)是它可以像一个临时的绑定到一个右值,而不必是const。因此,此语法现在是合法的:

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

如果这个构造函数被传递一个临时的,复制是不必要的,因为我们知道临时将被破坏;为什么不使用已经分配的临时资源?在C ++ 03中,没有办法防止复制,因为我们不能确定我们被传递了一个临时。在C ++ 0x中,我们可以重载一个移动构造函数:

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

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



move构造函数将用于临时和非const常量引用使用 std :: move 函数(它只是执行转换)显式转换为右值引用。以下代码调用 f1 f2 的移动构造函数:

  foo f1((foo())); //将临时数据移入f1;临时变为空
foo f2 = std :: move(f1); //将f1移动到f2; f1现在为空

完美转发。右值引用允许我们正确地转发模板函数的参数。以这个工厂函数为例:

  template< typename T,typename A1> 
std :: unique_ptr< T>工厂(A1和a1)
{
return std :: unique_ptr< T>(new T(a1));
}



如果我们调用 factory< foo> ,参数将被推导为 int& ,它不会绑定到文字5,即使 foo 的构造函数接受 int 。那么,我们可以使用 A1 const& ,但是如果 foo 通过非const引用接受构造函数参数呢?要创建一个真正的通用工厂函数,我们必须在 A1& A1 const& 如果工厂采用1个参数类型,那么这可能很好,但每个额外的参数类型都会将必要的重载设置乘以2.这是非常快速的不可维护的。



通过允许标准库定义一个 std :: forward 函数,可以正确转发lvalue / rvalue引用。有关 std :: forward 如何工作的详情,请参阅这个出色的答案



这使我们可以这样定义工厂函数:

  template< typename T,typename A1> 
std :: unique_ptr< T>工厂(A1& a1)
{
return std :: unique_ptr< T>(new T(std :: forward< A1&
}

现在参数的rvalue / lvalue- $ c> T 的构造函数。这意味着如果工厂以右值调用,则 T 的构造函数使用右值调用。如果工厂以左值调用,则 T 的构造函数将使用左值调用。改进的工厂函数的工作原理是一个特殊的规则:


当函数参数类型为
时,形式 T&&& 其中 T 是模板
参数,函数参数
是类型的左值 A ,类型 A&
用于模板参数扣除。


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

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

重要的右值引用属性




  • 对于重载解析, l值偏好结合到左值引用,r值偏好绑定到右值引用。因此,为什么临时者喜欢在复制构造函数/赋值运算符上调用移动构造函数/移动赋值运算符。

  • 右值引用将隐式绑定到右值和临时值隐式转换。即 float f = 0f;内部&& i = f; 是形成因为float是隐式可转换为int;引用将是转换结果的临时变量。

  • 命名的右值引用是左值。未命名的右值引用是右值。这很重要,以了解为什么 std :: move 调用是必要的: foo& r = foo(); foo f = std :: move(r);


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.

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.

解决方案

It declares an rvalue reference (standards proposal doc).

Here's an introduction to rvalue references.

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.)

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

T&& r = T();

rvalue references primarily provide for the following:

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.

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++0x, we can overload a move constructor:

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.

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:

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:

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.

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.

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:

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.

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.
  • 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.
  • 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);

这篇关于什么是T&amp; (双和符号)是什么意思在C ++ 11?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆