6

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

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

lref绑定到一个纯右值:

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

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

prvalue 不能获取其地址。但是对纯右值的左值引用可以获取它的地址,这对我来说很奇怪。

4

3 回答 3

6

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

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

临时字符串是调用者上下文中的纯右值(此时PrintAddress被调用)。在被调用者的上下文中 (in 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是一个左值(它有一个名字),所以你可以把它的地址加上它是可变的。

于 2017-03-12T20:11:58.583 回答
4

简而言之,因为prvalue的生命周期已经延长。通过延长它的生命周期 - 通过任何引用 - 它是一个左值,因此可以获取它的地址。


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

地址代表一个对象,该对象由lref.

prvalue 是短暂的,它不会长期存在。事实上,它会在创建它的语句结束时被销毁。

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

右值可用于初始化const lvalue [ rvalue ] 引用,在这种情况下,由右值标识的对象的生命周期会延长,直到引用范围结束

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

然而,获取纯右值的地址没有意义,这可能就是不允许它的原因:

  • 该值在下一条语句之后被销毁,因此您无法对地址执行任何操作,除非将其打印出来。

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

    因此,获取纯右值的地址将导致编译器无法完全忽略该值,而没有任何优势(参见第 1 点)。

于 2017-03-12T20:23:49.580 回答
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重载,因为它有一个 name,它是它范围内的“某物的左值” ,例如:

#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++ 中,任何具有名称的对象(无论是值类型引用类型还是指针类型)都是左值

也知道一些表达式也会产生左值

于 2017-03-12T20:16:41.953 回答