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

时间:2017-03-12 20:05:24

标签: c++ value-categories

当函数参数的类型为lvalue reference lref时:

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

lref绑定到prvalue:

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

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

prvalue无法获取其地址。但是左值引用一个prvalue 可以得到它的地址,这对我很好奇。

3 个答案:

答案 0 :(得分:5)

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

关于右撇子与左撇子存在一种常见的误解。 命名参数始终是左值。无论它是否是绑定到右值的引用类型。通过const &引用类型,您甚至无法确定对象在调用函数时实际具有哪种值类别。 Rvalue引用和非const Lvalue引用为您提供了这些信息:

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&#39;对象是&#34;总是&#34;那里。

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

答案 1 :(得分:4)

简而言之,因为prvalue的寿命已经延长。通过任何参考延长其寿命,它是一个左值,因此可以得到它的地址。

  

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

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

prvalue是短暂的,它不会长寿。实际上,当创建它的语句结束时它将被销毁。

但是,当您创建对prvalue(rvalue引用或const lvalue引用)的引用时,它的生命周期会被扩展。 Ref.:

  

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

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

然而,取一个prvalue的地址是没有意义的,这可能是为什么它被禁止的原因:

  • 该值在下一个语句后被销毁,因此您无法对该地址执行任何操作,除非将其打印出来。
  • 如果您获取某些内容的地址,则需要编译器实际创建该对象。有时,编译器会优化掉微不足道的变量,但是如果要获取它们的地址,则不允许编译器优化它们。

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

答案 2 :(得分:3)

用简单的英语:

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

任何具有名称的对象都是lvalue,因此在上述功能范围内使用lreflvalue使用。

使用以下方法调用函数时

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

原因是,lref.substr()生成一个临时的rvalue,但rvalues可以绑定(使其生命周期延长)const左值引用或右值引用。

即使你提供rvalue重载,因为它有一个名字,它的&#34;左右的内容&#34; 其范围,例如:

#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也会产生左值