对纯右值的左值引用的地址代表啥?

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&lt;string&amp;&gt;(static_cast&lt;cost string&amp;&gt;(s.substr(1))) = string() 一个临时住在那里。它们有生命周期,有时是可变的 “那里住着什么?” - 嗯? 【参考方案1】:

函数内部lref不是prvalue,它是一个左值,你可以取它的地址。

关于右值与左值存在一个常见的误解。 命名参数始终是左值。不管它是否是绑定到右值的引用类型。通过const &amp; 引用类型,您甚至无法判断对象在调用函数时实际具有哪种值类别。右值引用和非 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 被调用)。在被调用者的上下文中(PrintAddresslref 是一个左值引用,因为在这个上下文中它实际上是一个左值。

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 &amp; 到不应触及的特殊只读内存(尤其是编译时常量的事物/文字)中。 我认为 [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:不,我会说lrefreference 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 也产生左值

【讨论】:

以上是关于对纯右值的左值引用的地址代表啥?的主要内容,如果未能解决你的问题,请参考以下文章

[转]C++11 左值右值右值引用详解

C语言 啥叫做左值?右值?

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

C++11 中的左值引用和右值引用的区别

c++11:左值右值

第13课 右值引用