对纯右值的左值引用的地址代表啥?
Posted
技术标签:
【中文标题】对纯右值的左值引用的地址代表啥?【英文标题】:What does the address of an lvalue reference to a prvalue represent?对纯右值的左值引用的地址代表什么? 【发布时间】:2017-03-12 20:05:24 【问题描述】:当函数参数是左值类型时引用lref
:
void PrintAddress(const std::string& lref)
std::cout << &lref << std::endl;
并且lref
绑定到prvalue:
PrintAddress(lref.substr() /* temporary of type std::string */)
地址代表什么?那里住着什么?
prvalue 不能获取其地址。但是左值引用到纯右值可以获取它的地址,这对我来说很好奇。
【问题讨论】:
写入该地址是否合法(在 C++ 中,并且就内存而言)?例如,const_cast<string&>(static_cast<cost string&>(s.substr(1))) = string()
一个临时住在那里。它们有生命周期,有时是可变的
“那里住着什么?” - 嗯?
【参考方案1】:
函数内部lref
不是prvalue,它是一个左值,你可以取它的地址。
关于右值与左值存在一个常见的误解。
命名参数始终是左值。不管它是否是绑定到右值的引用类型。通过const &
引用类型,您甚至无法判断对象在调用函数时实际具有哪种值类别。右值引用和非 const 左值引用为您提供了这些信息:
void foo(std::string& L, std::string&& R)
// yeah i know L is already an lvalue at the point where foo is called
// R on the other hand is an rvalue at the point where we get called
// so we can 'safely' move from it or something...
临时字符串是调用者上下文中的prvalue(在点PrintAddress
被调用)。在被调用者的上下文中(PrintAddress
)lref
是一个左值引用,因为在这个上下文中它实际上是一个左值。
PrintAddress
不知道传递的参数的有限生命周期,从PrintAddress
' 的角度来看,对象“总是”在那里。
std::string q("abcd");
PrintAddress(q.substr(1)); // print address of temporary
在概念上等同于:
std::string q("abcd");
const std::string& lref = q.substr(1);
std::cout << &lref << std::endl;
临时对象的生命周期延长到定义 lref
的范围的末尾(在本示例中为 PrintAddress
函数范围的末尾)。
地址代表什么?那里住着什么?
一个包含传递内容的std::string
对象。
写入该地址是否合法(在 C++ 中,并且就内存而言)?
不,如果你使用右值引用是合法的:
void PrintAddressR(std::string&& rref)
rref += "Hello"; // writing possible
std::cout << &rref << std::endl; // taking the address possible
// ...
PrintAddressR(q.substr(1)); // yep, can do that...
这同样适用:rref
是一个左值(它有一个名称),因此您可以获取它的地址并且它是可变的。
【讨论】:
你有非法修改的来源吗?绑定到 const 引用会取消修改的资格是有道理的,但我认为有合理的机会并非如此。为什么我说合理是因为你可以做一些事情,比如在prvalues上调用非常量成员函数。 所以准确地说,您会说lref
不是 lvalue reference to prvalue
,而是lvalue reference to const string, *initialized with* the prvalue
?
@chris:不,我没有,我不认为我现在可以在标准中查找它,但我认为没有任何东西阻止编译器放置临时绑定到 const &
到不应触及的特殊只读内存(尤其是编译时常量的事物/文字)中。
我认为 [conv.rval] 会涵盖这一点。 T 类型的纯右值可以转换为 T 类型的 xvalue。 prvalue 的类型为 std::string
,因此物化的 xvalue 对象的类型为 std::string
,因此是可修改的。 [dcl.init.ref] 当prvalue 绑定到const 引用时调用实现。这样做的背景是,执行std::string s; foo(s);
将允许foo
的 const 引用丢弃 const 并对其进行修改。
@mgiuffrida:不,我会说lref
是reference to const string to which a prvalue is bound
。【参考方案2】:
简而言之,因为prvalue的生命周期已经延长。通过延长它的生命周期 - 通过任何引用 - 它是一个左值,因此可以获取它的地址。
地址代表什么?那里住着什么?
地址代表一个对象,lref
引用的对象。
prvalue 是短暂的,它不会长期存在。事实上,它会在创建它的语句结束时被销毁。
但是,当您创建对纯右值的引用(右值引用或 const 左值引用)时,它的生命周期会延长。 Ref.::
rvalue 可用于初始化 const lvalue [rvalue] 引用,在这种情况下,对象的生命周期由右值被延伸到引用范围结束。
现在获取它的地址实际上是有意义的,因为它是所有意图和目的的左值。现在,prvalue 有一个不确定的生命周期,它是一个左值。
然而,获取纯右值的地址没有意义,这可能就是不允许它的原因:
值在下一个语句之后被销毁,所以你不能对地址做任何事情,除非打印出来。
如果您获取某物的地址,则需要编译器来实际创建该对象。有时,编译器会优化掉一些微不足道的变量,但如果你要取它们的地址,编译器将不允许优化掉它们。
因此,获取纯右值的地址将导致编译器无法完全删除该值,没有任何好处(见第 1 点)。
【讨论】:
普遍赞成,但我认为这有助于解释可能的动机 为什么 取lref
的地址而不是prvalue
的地址是有意义的.谢谢!【参考方案3】:
简单的英语:
void PrintAddress(const std::string& lref)
std::cout << &lref << std::endl;
任何有名字的对象都是lvalue
,因此在上述函数范围内对lref
的任何使用都是lvalue
使用。
当你调用函数时:
PrintAddress(lref.substr() /* temporary of type std::string */)
当然,lref.substr()
会生成一个临时的 rvalue
,但 rvalues
可以绑定到(延长其生命周期)const 左值引用或右值引用。
即使你提供了一个rvalue
重载,因为它有一个名字,它是一个在其范围内的“某物的左值”,例如:
#include <string>
#include <iostream>
void PrintAddress(const std::string& lref)
std::cout << "LValue: " << &lref << std::endl;
void PrintAddress(std::string&& `rref`)
std::cout << "RValue: " << &rref << std::endl; //You can take address of `rref`
int main()
std::string str = "Hahaha";
PrintAddress(str);
PrintAddress(str.substr(2));
记住:
在 C++ 中,任何对象(无论是值类型、引用类型还是指针类型) name 是一个 左值
也知道some expressions 也产生左值。
【讨论】:
以上是关于对纯右值的左值引用的地址代表啥?的主要内容,如果未能解决你的问题,请参考以下文章