【问题标题】:How is it possible to get a reference to an rvalue?如何获得对右值的引用?
【发布时间】:2015-02-11 15:51:25
【问题描述】:

我在 C++ 中使用过std::movestd::forward。我的问题是:标准库实际上是如何实现这些功能的?

如果一个左值是你可以得到地址的东西,而一个右值完全不是一个左值,你怎么能真正实现这些引用?

这些新设施是否允许:

auto x = &(3); 

或类似的东西?您能否获得对不只是 std::move/forward 返回左值的右值的引用?

希望这些问题是有意义的。我在谷歌上找不到好的信息,只有完美转发的教程等。

【问题讨论】:

  • 带有右值引用 :P 这意味着你没有搜索 :(
  • 当然,我怎么没想到呢?

标签: c++ c++11 rvalue-reference


【解决方案1】:

如何获得对右值的引用?

从概念上讲,rvalue 表达式创建一个临时对象,或者有时表示一个现有对象。可以像任何其他对象一样绑定到引用;但是,为了避免混淆,该语言只允许 rvalueconst lvalue 引用。

我在 C++ 中使用过 std::move 和 std::forward。我的问题是编译器实际上是如何实现的?

move 只返回一个 rvalue 对其参数的引用,相当于

static_cast<typename remove_reference<T>::type&&>(t)

函数调用的结果是一个rvalue(具体来说,一个xvalue),所以它可以绑定到一个rvalue引用,其中函数参数不能。这允许您从 左值 显式移动,使用 move 将其转换为 右值,同时不允许您意外地从它移动。

forward 类似,但重载以返回对 rvaluervalue 引用的 rvalue 引用和 lvalue 引用其他任何东西。

如果一个左值是你可以得到的地址

这或多或少是正确的。官方的定义是“指定一个函数或一个对象”的表达,那些是有地址的东西。

并且右值完全不是左值

不是真的。稍微简化一下,表达式可以是 lvaluervalue,但可以从一个转换为另一个。 lvalue 可以隐式转换为 rvalue;像move 所做的那样,可以通过强制转换来转换另一种方式。

如何实际实现这些引用?

就像任何其他引用一样 - 作为它绑定到的对象的别名或指针。唯一的区别是可以使用哪些类型的表达式来表示(并可能创建)绑定到引用的对象。

这些新设施是否允许类似auto x = &amp;(3);

试图直接获取 rvalue 的地址,这是不允许的。由于问题是关于引用,而不是指针,因此允许将引用绑定到临时对象(其生命周期被延长以匹配引用):

auto && rvalue = 3;
auto const & const_lvalue = 3;

虽然不允许将其绑定到非 const lvalue 引用

auto & lvalue = 3;  // ERROR

【讨论】:

    【解决方案2】:

    我不能像这样调用函数:void foo(string* bar)foo(&amp;string("Hello World!")),否则我会收到错误:

    错误:获取临时地址

    我也不能像这样调用函数:void foo(string&amp; bar)foo(string("Hello World!")),否则我得到一个错误:

    错误:从“std::string {aka std::basic_string}”类型的右值对“std::string& {aka std::basic_string&}”类型的非常量引用无效初始化

    C++11 提供给我的能力是创建一个右值引用,所以我可以调用一个函数:void foo(string&amp;&amp; bar),像这样:foo(string("Hello World!"));

    另外,在foo内部,我可以得到右值引用传入的对象的地址:

    void foo(string&& bar){
        string* temp = &bar;
    
        cout << *temp << " @:" << temp << endl;
    }
    

    似乎 OP 对右值有很好的把握。但是this explanation of them 对我有帮助,可能对其他人也有帮助。它详细介绍了为什么 C++03 允许对右值进行常量引用,而不是 C++11 的右值引用。

    【讨论】:

    • 这绝对回答了我所有的问题。谢谢你。编辑:谢谢你的链接,我看看。
    • “此外,在foo 内部,我可以得到右值的地址:” 我不同意:右值,或者一般的表达式,没有地址- 对象有地址。因此,&amp;bar 不会为您提供右值的地址。您将获得由表达式 bar&amp;bar 的子表达式)引用的对象的地址,而此表达式 bar 是一个左值。
    • @dyp 好的,我同意你的说法......你对这个编辑感到满意吗:“此外,在 foo 内部,我可以通过一个r 值参考。”
    • 嗯,我不认为这是不正确或不精确的。但它可能没有涵盖如何实现它以获取对象地址的本质:您需要一个引用对象的左值来获取其地址,因此您更改表达式的值类别,或者更确切地说,创建一个引用同一对象的新左值表达式。比如反招template&lt;class T&gt; T&amp; as_lvalue(T&amp;&amp; t) { return t; }
    • @dyp 你的“反移动”很有趣。我从来没有想过这样做。我已经进行了第一次编辑......但是当你说:“你需要一个引用对象的左值来获取它的地址时,请帮助我。”你是说bar 实际上是foo 内部的左值吗?
    【解决方案3】:

    基本上,编译器的魔法。标准描述了规则,编译器制造者只需要弄清楚如何实现规则。

    实际上,引用要么被优化,要么被实现为 CPU 级别的指针。

    std::move 在这个意义上并不特别。它有一个左值引用作为输入,一个右值引用作为输出。编译器只需要将右值引用规则应用于输入。

    同样,std::forward&lt;T&gt; 的目标只是告诉编译器对参数应用一组不同的规则,这些规则恰好被定义为完美转发工作。函数本身什么都不做。

    【讨论】:

    • 对,我知道 std::move 和 std::forward 基本上就像“取一个 L 值,把它当作一个 r 值”。但是是否有可能真正获得“纯”r 值的地址?就像自动 x = &(i + 3);我猜。 可能吗?我仍然认为不会,对吧?编辑:你已经回答了我原来的问题。我应该提出一个主要问题,即我也对在没有 std::move/std::forward 的情况下获得 r 值引用感到好奇
    • "是否有可能实际获得一个“纯”右值的地址?"是的,通过创建一个临时对象来保存该值。该对象有一个地址。该语言不允许您直接使用&amp; 获取地址,但您可以将其绑定到各种类型的引用。要在没有 move 的情况下获得对 lvaluervalue 引用,您需要进行强制转换(这就是 move 所做的一切)。
    猜你喜欢
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多