Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
434 views
in Technique[技术] by (71.8m points)

c++ - T &&(双“&”号)在C ++ 11中是什么意思?(What does T&& (double ampersand) mean in C++11?)

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的一些新功能,我注意到的一个是在声明变量(例如T&& var )时使用双“&”号。)

For a start, what is this beast called?

(首先,这只野兽叫什么?)

I wish Google would allow us to search for punctuation like this.

(我希望Google允许我们搜索这样的标点符号。)

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 ),但是我很难考虑到这种情况。)

  ask by paxdiablo translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 .

(这是Microsoft标准库开发人员之一对rvalue引用进行的精彩深入研究。)

CAUTION: the linked article on MSDN ("Rvalue References: C++0x Features in VC10, Part 2") is a very clear introduction to Rvalue references, but makes statements about Rvalue references that were once true in the draft C++11 standard, but are not true for the final one!

(注意: MSDN上的链接文章(“ Rvalue参考:VC10中的C ++ 0x功能,第2部分”)是对Rvalue引用的非常清晰的介绍,但是对有关Rvalue引用的声明在C ++ 11草案中曾经是正确的。标准,但对于最后一个不是正确的!)

Specifically, it says at various points that rvalue references can bind to lvalues, which was once true, but was changed.(eg int x; int &&rrx = x; no longer compiles in GCC) – drewbarbs Jul 13 '14 at 16:12

(具体来说,它说在各个点上右值引用可以绑定到左值,这曾经是真实的,但是已经被更改。(例如int x; int && rrx = x;不再在GCC中编译)– drewbarbs 2014年7月13日在16:12)

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.

(C ++ 03引用(在C ++ 11中现在称为左值引用)之间的最大区别在于,它可以像临时元素一样绑定到右值,而不必使用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.

(现在可以定义移动构造函数和移动赋值运算符,该运算符采用右值引用而不是通常的const-左值引用。)

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.

(在C ++ 03中,由于无法确定我们是否被临时传递,因此无法阻止复制。)

In C++11, we can overload a move constructor:

(在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.

(注意这里的最大区别:move构造函数实际上是修改其参数。)

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

(move构造函数将用于临时对象和非常量左值引用,这些引用使用std::move函数(仅执行转换)显式转换为右值引用。)

The following code both invoke the move constructor for f1 and f2 :

(以下代码均调用f1f2的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:

(以这个工厂功能为例:)

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 .

(如果我们调用factory<foo>(5) ,则将推导该参数为int& ,即使foo的构造函数采用int ,该参数也不会绑定到文字5。)

Well, we could instead use A1 const& , but what if foo takes the constructor argument by non-const reference?

(好吧,我们可以改用A1 const& ,但是如果foo通过非const引用接受构造函数参数怎么办?)

To make a truly generic factory function, we would have to overload factory on A1& and on A1 const& .

(为了实现真正的通用工厂功能,我们必须在A1&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采用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.

(右值引用通过允许标准库定义可以正确转发左值/右值引用的std::forward函数来解决此问题。)

For more information about how std::forward works, see this excellent answer .

(有关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.

(现在,当传递给T的构造函数时,参数的rvalue / lvalue-ness得以保留。)

That means that if factory is called with an rvalue, T 's constructor is called with an rvalue.

(这意味着,如果使用rvalue调用factory,则使用rvalue调用T的构造函数。)

If factory is called with an lvalue, T 's constructor is called with an lvalue.

(如果使用左值调用factory,则使用左值调用T的构造函数。)

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.

(当函数参数类型的形式为T&& ,其中T是模板参数,并且函数参数是类型A的左值时,类型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.

    (因此,为什么临时人员比复制构造函数/赋值运算符更喜欢调用移动构造函数/移动赋值运算符。)

  • rvalue references will implicitly bind to rvalues and to temporaries that are the result of an implicit conversion .

    (右值引用将隐式绑定到右值和作为隐式转换结果的临时对象 。)

    ie float f = 0f; int&& i = f;

    (即float f = 0f; int&& i = f;) float f = 0f; int&& i = f; is well formed because float is implicitly convertible to int;

    (格式良好,因为float可以隐式转换为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);

    (了解为什么为什么必须在以下位置进行std::move调用非常重要: foo&& r = foo(); foo f = std::move(r);) foo&& r = foo(); foo f = std::move(r);


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...