T&&; 是什么意思? (双&符号)在 C++11 中意味着什么?
我一直在研究 C++11 的一些新功能,我注意到的一个是声明变量时的双 & 符号,例如 T&&变量
。
首先,这个野兽叫什么?我希望谷歌允许我们像这样搜索标点符号。
它到底是什么意思?
乍一看,它似乎是一个双重引用(如 C 风格的双指针 T** var
),但我有很难想到它的用例。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
右值引用是一种行为方式与普通引用 X& 非常相似的类型,但有几个例外。最重要的是,当涉及到函数重载解析时,左值更喜欢旧式的左值引用,而右值更喜欢新的右值引用:
那么什么是右值?任何不是左值的东西。左值存在
一个引用内存位置的表达式,允许我们通过 & 获取该内存位置的地址。操作员。
首先通过一个示例来理解右值完成的任务几乎更容易:
构造函数和赋值运算符已被采用右值引用的版本重载。右值引用允许函数在编译时(通过重载决策)在“我是在左值还是右值上被调用?”的条件下进行分支。 这使我们能够在上面创建更高效的构造函数和赋值运算符,以移动资源而不是复制它们。
编译器在编译时自动分支(取决于是否为左值或右值调用)选择是否应调用移动构造函数或移动赋值运算符。
总结:右值引用允许移动语义(以及完美转发,在下面的文章链接中讨论)。
一个易于理解的实用示例是类模板 std::unique_ptr。由于 unique_ptr 维护其底层原始指针的独占所有权,因此无法复制 unique_ptr。这将违反他们的专有所有权不变性。所以他们没有复制构造函数。但它们确实有移动构造函数:
static_cast&&>(ptr)
通常使用 std::move一篇优秀的文章Thomas Becker 的 C++ 右值引用解释。这篇文章很大程度上依赖于他的文章。
较短的介绍是 Stroutrup 等人的右值引用简介。阿尔
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:
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.
It is almost easier to understand first what rvalues accomplish with an example:
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.
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.
Summing up: rvalue references allow move semantics (and perfect forwarding, discussed in the article link below).
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:
static_cast<unique_ptr<int[]>&&>(ptr)
is usually done using std::moveAn 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.
A shorter introduction is A Brief Introduction to Rvalue References by Stroutrup, et. al
它声明了一个右值参考(标准提案文档)。
下面是对右值参考的介绍。
这是对 Microsoft 标准库之一的右值引用的精彩深入了解 开发人员。
C++03 引用(现在在 C++11 中称为左值引用)之间的最大区别在于,它可以像临时变量一样绑定到右值,而不必是 const。因此,这种语法现在是合法的:
右值引用主要提供以下内容:
移动语义。现在可以定义移动构造函数和移动赋值运算符,它们采用右值引用而不是通常的 const-左值引用。移动的功能类似于复制,只不过它不必保持源不变;事实上,它通常会修改源,使其不再拥有移动的资源。这对于消除无关副本非常有用,尤其是在标准库实现中。
例如,复制构造函数可能如下所示:
如果此构造函数传递了临时值,则复制将是不必要的,因为我们知道临时值将被销毁;为什么不利用临时已经分配的资源呢?在 C++03 中,没有办法阻止复制,因为我们无法确定是否传递了临时值。在 C++11 中,我们可以重载移动构造函数:
请注意这里的巨大区别:移动构造函数实际上修改了其参数。这将有效地将临时“移动”到正在构造的对象中,从而消除不必要的复制。
移动构造函数将用于临时变量和非常量左值引用,这些引用使用 std::move 函数显式转换为右值引用(它只执行转换)。以下代码均调用
f1
和f2
的移动构造函数:完美转发。右值引用允许我们正确转发模板函数的参数。以这个工厂函数为例:
如果我们调用
factory(5)
,参数将被推断为int&
,它不会绑定到文字 5 ,即使foo
的构造函数采用int
。好吧,我们可以使用A1 const&
,但是如果foo
通过非常量引用获取构造函数参数怎么办?为了创建一个真正通用的工厂函数,我们必须在A1&
和A1 const&
上重载工厂。如果工厂采用 1 个参数类型,这可能没问题,但每个附加参数类型都会将必要的重载设置乘以 2。这很快就无法维护。右值引用通过允许标准库定义一个可以正确转发左值/右值引用的 std::forward 函数来解决此问题。有关
std::forward
工作原理的更多信息,请参阅这个出色的答案。这使我们能够像这样定义工厂函数:
现在,当传递给 T 的构造函数时,参数的右值/左值性质被保留。这意味着如果使用右值调用工厂,则使用右值调用
T
的构造函数。如果使用左值调用工厂,则使用左值调用T
的构造函数。改进的工厂函数之所以有效,是因为一个特殊的规则:因此,我们可以像这样使用工厂:
重要的右值引用属性:
foo&& 中需要
std::move
调用非常重要。 r = foo(); foo f = std::move(r);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.
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:
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:
If this constructor were 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 whether we were passed a temporary. In C++11, we can overload a move constructor:
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 forf1
andf2
:Perfect forwarding. rvalue references allow us to properly forward arguments for templated functions. Take for example this factory function:
If we called
factory<foo>(5)
, the argument will be deduced to beint&
, which will not bind to a literal 5, even iffoo
's constructor takes anint
. Well, we could instead useA1 const&
, but what iffoo
takes the constructor argument by non-const reference? To make a truly generic factory function, we would have to overload factory onA1&
and onA1 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 howstd::forward
works, see this excellent answer.This enables us to define the factory function like this:
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:Thus, we can use factory like so:
Important rvalue reference properties:
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.std::move
call is necessary in:foo&& r = foo(); foo f = std::move(r);
它表示右值引用。右值引用只会绑定到临时对象,除非明确生成。它们用于使对象在某些情况下更加高效,并提供称为完美转发的功能,从而大大简化了模板代码。
在 C++03 中,您无法区分不可变左值和右值的副本。
在 C++0x 中,情况并非如此。
考虑这些构造函数背后的实现。在第一种情况下,字符串必须执行复制以保留值语义,这涉及新的堆分配。然而,在第二种情况下,我们事先知道传递给构造函数的对象将立即被销毁,并且它不必保持不变。在这种情况下,我们可以有效地只交换内部指针,而不执行任何复制,这实际上更加有效。移动语义有利于任何具有昂贵或禁止复制内部引用资源的类。考虑 std::unique_ptr 的情况 - 既然我们的类可以区分临时对象和非临时对象,我们就可以使移动语义正确工作,这样
unique_ptr
就不能被复制但可以移动,这意味着 std::unique_ptr 可以合法地存储在标准容器中、排序等,而 C++03 的 std::auto_ptr 则不能。现在我们考虑右值引用的另一种用途——完美转发。考虑将引用绑定到引用的问题。
不记得 C++03 对此有何评论,但在 C++0x 中,处理右值引用时的结果类型至关重要。对类型 T 的右值引用(其中 T 是引用类型)将成为类型 T 的引用
。考虑最简单的模板函数 - min 和 max。在 C++03 中,您必须手动重载 const 和非常量的所有四种组合。在 C++0x 中,这只是一种重载。与可变参数模板相结合,可以实现完美转发。
我省略了返回类型推导,因为我不记得它是如何立即完成的,但 min 可以接受左值、右值、const 左值的任意组合。
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.
In C++03, you can't distinguish between a copy of a non-mutable lvalue and an rvalue.
In C++0x, this is not the case.
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 theunique_ptr
cannot be copied but can be moved, which means thatstd::unique_ptr
can be legally stored in Standard containers, sorted, etc, whereas C++03'sstd::auto_ptr
cannot.Now we consider the other use of rvalue references- perfect forwarding. Consider the question of binding a reference to a reference.
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.
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.
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.
T&&
术语当与类型推导一起使用时(例如完美转发)通俗地称为转发引用。 “通用参考”一词是由 Scott Meyers 在此创造的文章,但后来被更改。这是因为它可能是右值或左值。
示例如下:
更多讨论可以在以下答案中找到:通用引用的语法
The term for
T&&
when used with type deduction (such as for perfect forwarding) is known colloquially as a forwarding reference. The term "universal reference" was coined by Scott Meyers in this article, but was later changed.That is because it may be either r-value or l-value.
Examples are:
More discussion can be found in the answer for: Syntax for universal references