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

What does the address of an lvalue reference to a prvalue represent?

当函数参数是左值引用类型时lref:

void PrintAddress(const std::string& lref) {
  std::cout << &lref << std::endl;
}

lref 绑定到纯右值:

PrintAddress(lref.substr() /* temporary of type std::string */)

地址代表什么?那里住着什么?

纯右值的地址不能被占用。但是左值引用 to 纯右值 can 的地址被占用,这让我很好奇。

函数内部 lref 不是纯右值,它是左值,您可以获取它的地址。

对于右值与左值存在一种常见的误解。 命名参数始终是左值。不管它是否是绑定到右值的引用类型。通过 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...
}

临时字符串是调用者上下文中的纯右值(在 PrintAddress 被调用时)。在被调用者的上下文中(在 PrintAddress 中),lref 是一个左值引用,因为在这个上下文中它实际上是一个左值。

PrintAddress 不知道传递参数的有限生命周期,从 PrintAddress 的角度来看,对象 "always" 在那里。

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 函数范围的末尾)。


what does the address represent? What lives there?

包含传递内容的 std::string 对象。

And is it legal (in C++, and with respect to memory) to write to that address?

不,如果您使用右值引用是合法的:

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 是一个左值(它有一个名称)所以你可以获取它的地址加上它是可变的。

简单的英语:

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 重载,因为它有一个名称,它是一个 "lvalue of something"在其范围内,例如:

#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));
}

记住:

In C++, any object(whether value type, reference type or pointer type) that has a name is an lvalue

也知道 some expressions 也产生 左值

简而言之,因为纯右值的生命周期被延长了。通过延长它的生命周期——通过任何引用——它是一个左值,因此它的地址可以被占用。


what does the address represent? What lives there?

地址代表一个对象,lref引用的对象。

纯右值是短暂的,它不会长寿。事实上,它会在创建它的语句结束时被销毁。

但是,当您创建对纯右值的引用(右值引用或 const 左值引用)时,它的生命周期会延长。 Ref.::

An rvalue may be used to initialize a const lvalue [rvalue] reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.

现在获取它的地址实际上是有意义的,因为它是所有意图和目的的左值。现在,纯右值具有不确定的生命周期,它是一个左值。

获取纯右值的地址没有任何意义,这可能就是它被禁止的原因:

  • 值在下一条语句后被销毁,所以你不能对地址做任何事情,除非打印出来。

  • 如果你取某物的地址,编译器需要实际创建对象。有时,编译器会优化出一些微不足道的变量,但如果你要获取它们的地址,编译器将不允许优化它们。

    获取纯右值的地址将导致编译器无法完全省略该值,没有任何好处(请参阅第 1 点)。