如何获得对右值的引用?
Posted
技术标签:
【中文标题】如何获得对右值的引用?【英文标题】:How is it possible to get a reference to an rvalue? 【发布时间】:2015-02-11 15:51:25 【问题描述】:我在 C++ 中使用过std::move
和std::forward
。我的问题是:标准库实际上是如何实现这些功能的?
如果一个左值是你可以得到地址的东西,而一个右值完全不是一个左值,你怎么能真正实现这些引用?
这些新设施是否允许:
auto x = &(3);
或类似的东西?您能否获得对不只是 std::move
/forward
返回左值的右值的引用?
希望这些问题是有意义的。我在谷歌上找不到好的信息,只有完美转发的教程等。
【问题讨论】:
带有右值引用 :P 这意味着你没有搜索 :( 当然,我怎么没想到呢? 【参考方案1】:如何获得对右值的引用?
从概念上讲,rvalue 表达式创建一个临时对象,或者有时表示一个现有对象。可以像任何其他对象一样绑定到引用;但是,为了避免混淆,该语言只允许 rvalue 和 const
lvalue 引用。
我在 C++ 中使用过 std::move 和 std::forward。我的问题是编译器实际上是如何实现的?
move
只返回一个 rvalue 对其参数的引用,相当于
static_cast<typename remove_reference<T>::type&&>(t)
函数调用的结果是一个rvalue(具体来说,一个xvalue),所以它可以绑定到一个rvalue引用,其中函数参数不能。这允许您从 左值 显式移动,使用 move
将其转换为 右值,同时不允许您意外地从它移动。
forward
类似,但重载以返回对 rvalue 或 rvalue 引用的 rvalue 引用和 lvalue 引用其他任何东西。
如果一个左值是你可以得到的地址
这或多或少是正确的。官方的定义是“指定一个函数或一个对象”的表达,那些是有地址的东西。
并且右值完全不是左值
不是真的。稍微简化一下,表达式可以是 lvalue 或 rvalue,但可以从一个转换为另一个。 lvalue 可以隐式转换为 rvalue;像move
所做的那样,可以通过强制转换来转换另一种方式。
如何实际实现这些引用?
就像任何其他引用一样 - 作为它绑定到的对象的别名或指针。唯一的区别是可以使用哪些类型的表达式来表示(并可能创建)绑定到引用的对象。
这些新设施是否允许类似
auto x = &(3);
试图直接获取 rvalue 的地址,这是不允许的。由于问题是关于引用,而不是指针,因此允许将引用绑定到临时对象(其生命周期被延长以匹配引用):
auto && rvalue = 3;
auto const & const_lvalue = 3;
虽然不允许将其绑定到非 const lvalue 引用
auto & lvalue = 3; // ERROR
【讨论】:
【参考方案2】:我不能像这样调用函数:void foo(string* bar)
:foo(&string("Hello World!"))
,否则我会收到错误:
错误:获取临时地址
我也不能像这样调用函数:void foo(string& bar)
:foo(string("Hello World!"))
,否则我得到一个错误:
错误:从“std::string aka std::basic_string”类型的右值对“std::string& aka std::basic_string&”类型的非常量引用无效初始化
C++11 提供给我的能力是创建一个右值引用,所以我可以调用一个函数:void foo(string&& 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
内部,我可以得到右值的地址:” 我不同意:右值,或者一般的表达式,没有地址- 对象有地址。因此,&bar
不会为您提供右值的地址。您将获得由表达式 bar
(&bar
的子表达式)引用的对象的地址,而此表达式 bar
是一个左值。
@dyp 好的,我同意你的说法......你对这个编辑感到满意吗:“此外,在 foo
内部,我可以通过一个r 值参考。”
嗯,我不认为这是不正确或不精确的。但它可能没有涵盖如何实现它以获取对象地址的本质:您需要一个引用对象的左值来获取其地址,因此您更改表达式的值类别,或者更确切地说,创建一个引用同一对象的新左值表达式。比如反招template<class T> T& as_lvalue(T&& t) return t;
@dyp 你的“反移动”很有趣。我从来没有想过这样做。我已经进行了第一次编辑......但是当你说:“你需要一个引用对象的左值来获取它的地址时,请帮助我。”你是说bar
实际上是foo
内部的左值吗?【参考方案3】:
基本上,编译器的魔法。标准描述了规则,编译器制造者只需要弄清楚如何实现规则。
实际上,引用要么被优化,要么被实现为 CPU 级别的指针。
std::move
在这个意义上并不特别。它有一个左值引用作为输入,一个右值引用作为输出。编译器只需要将右值引用规则应用于输入。
同样,std::forward<T>
的目标只是告诉编译器对参数应用一组不同的规则,这些规则恰好被定义为完美转发工作。函数本身什么都不做。
【讨论】:
对,我知道 std::move 和 std::forward 基本上就像“取一个 L 值,把它当作一个 r 值”。但是是否有可能真正获得“纯”r 值的地址?就像自动 x = &(i + 3);我猜。 那可能吗?我仍然认为不会,对吧?编辑:你已经回答了我原来的问题。我应该提出一个主要问题,即我也对在没有 std::move/std::forward 的情况下获得 r 值引用感到好奇 "是否有可能实际获得一个“纯”右值的地址?"是的,通过创建一个临时对象来保存该值。该对象有一个地址。该语言不允许您直接使用&
获取地址,但您可以将其绑定到各种类型的引用。要在没有 move
的情况下获得对 lvalue 的 rvalue 引用,您需要进行强制转换(这就是 move
所做的一切)。以上是关于如何获得对右值的引用?的主要内容,如果未能解决你的问题,请参考以下文章