混淆了r值和l值

Confused with r-values and l-values

本文关键字:值和      更新时间:2023-10-16

我最近在c++ 11的白皮书上读到,如果我写,

void foo(X&)

对于l值将调用该函数,而对于r值

则不调用。

如果我写

void foo(const X&)

对于r值和l值都将调用。

谁能给我举个例子这是什么意思?

这个函数接受一个左值引用

void foo(X&)

并尝试用右值调用它会产生错误。

这个函数需要一个const左值引用

void foo(const X&)

但是有一个陷阱:

右值可以用来初始化const左值引用,在这种情况下,由右值标识的对象的生命周期被延长,直到引用的作用域结束。

从cppreference

所以最后一个函数同时接受左值和右值形参

struct X {};
void foo(X&) {
}
void fooC(const X&) {
}
int main() { 
   X obj;
   // foo(std::move(obj)); // Error - non-const
   foo(obj); // Valid
   fooC(obj); // Valid
   fooC(std::move(obj)); // Valid
}
参考初始化或检查[dcl.init.ref]/p5

考虑以下代码:

int i = 58;

什么是58 ?它是一个常量

因此:foo (const int&)

58是一个r值(在这种情况下也是一个文字常量),因为你不能直接引用它,比如说,修改它,因为没有办法这样做。您可以引用i,因为它有一个名称,一个标识符,但58本身没有标识符。

通俗地说,如果某个东西存在,但它没有声明的名称,你可以用它来访问它,那么它就是一个常量和一个r值,除非你用r-val引用它,否则你可以改变它。

int f();
...
int x = f(); //the returned int is an r-value, but x is an l-value

当你声明foo函数

void foo(X&)

那么这样的代码将无法编译:

foo(5);

但是如果你这样声明:

void foo(const X&)

,那么你就可以这样调用它:

foo(5);

const literal 5是一个r值,因为它没有名称。

大致说来,右值是临时对象(从函数返回),而左值也是可以引用的对象。

例如:

X bar();
X x = bar();

在该代码中(忽略可能的编译器优化),创建了一个临时X对象来保存foo的返回值并传递给x的复制构造函数,这个临时对象是一个右值,只存在于该语句中,不能被代码的其他部分引用。然而,x是一个左值,您可以检索它的地址并稍后引用它。

两个函数

void foo(X &);
void foo(const X &);

分别期望左值、非const和const。后者之所以接受右值,是因为右值可以转换为对左值的const引用。也就是说,您可以这样做:

const X &ref = bar();

在这里,bar()返回的临时变量直到ref离开作用域才被销毁,所以它的生命周期在语句之外被延长了。但是,对于非const引用,

不能这样做。
X &ref = bar(); // Error!

这是不允许的,因为bar的返回值是一个临时值(右值),不能修改。

请注意,这不是c++ 11的特性,它已经存在了。新特性是能够指定右值引用

void foo(X &&);
void foo(const X &&);

引入了更微妙的情况,例如参见c++ 11中的通用引用- Scott Meyers。