混淆右值和左值
Confused with r-values and l-values
我最近在 C++11 的白皮书上读到,如果我写,
void foo(X&)
这将针对左值而不是右值调用
如果我写,
void foo(const X&)
这将为 r 值和 l 值调用。
谁能给我举个例子,这是什么意思?
这个函数接受左值引用
void foo(X&)
尝试用右值调用它会产生错误。
此函数需要一个 const
左值引用
void foo(const X&)
但有一个问题:
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
来自 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
}
有关更多信息,请查看此处:reference initialization 或查看 [dcl.init.ref]/p5
考虑这段代码:
int i = 58;
什么是58
?它是一个文字常量.
因此:foo (const int&)
58
是一个右值(在这种情况下也是一个文字常量),因为您不能直接引用它,比方说,修改它,因为没有办法这样做。你可以参考i
,因为它有名字,有标识符,但是58
本身没有标识符。
通俗地说,如果某物存在但没有声明的名称,您可以使用它访问它,那么它是一个常量和一个 r 值,除非您使用 r-val 引用 int&& 引用它,然后您可以改变它。
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 文字 5 是一个右值,因为它没有名称。
粗略地说,右值是临时对象(从函数返回),而左值是您也可以引用的对象。
例如:
X bar();
X x = bar();
在该代码中(忽略可能的编译器优化),创建了一个临时 X 对象来保存 foo 的返回值并传递给 x
的复制构造函数,这个临时对象是一个右值,仅存在于此语句中,不能被代码的其他部分引用。 x
不过是一个左值,你可以取回它的地址,以后再引用。
两种功能
void foo(X &);
void foo(const X &);
分别期望左值、非常量和常量。后者接受右值的原因是因为右值可以转换为对左值的常量引用。也就是说,你可以这样做:
const X &ref = bar();
这里 bar() 返回的临时变量直到 ref
超出范围才被销毁,因此它的生命周期已经延长到语句之外。但是你不能对非常量引用做同样的事情,
X &ref = bar(); // Error!
这是不允许的,因为 bar 的返回值是临时值(右值),无法修改。
但是请注意,这不是 C++11 的功能,它已经存在了。新功能是能够指定右值引用
void foo(X &&);
void foo(const X &&);
引入了更微妙的情况,例如 Universal References in C++11 - Scott Meyers。
我最近在 C++11 的白皮书上读到,如果我写,
void foo(X&)
这将针对左值而不是右值调用
如果我写,
void foo(const X&)
这将为 r 值和 l 值调用。
谁能给我举个例子,这是什么意思?
这个函数接受左值引用
void foo(X&)
尝试用右值调用它会产生错误。
此函数需要一个 const
左值引用
void foo(const X&)
但有一个问题:
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
来自 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
}
有关更多信息,请查看此处:reference initialization 或查看 [dcl.init.ref]/p5
考虑这段代码:
int i = 58;
什么是58
?它是一个文字常量.
因此:foo (const int&)
58
是一个右值(在这种情况下也是一个文字常量),因为您不能直接引用它,比方说,修改它,因为没有办法这样做。你可以参考i
,因为它有名字,有标识符,但是58
本身没有标识符。
通俗地说,如果某物存在但没有声明的名称,您可以使用它访问它,那么它是一个常量和一个 r 值,除非您使用 r-val 引用 int&& 引用它,然后您可以改变它。
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 文字 5 是一个右值,因为它没有名称。
粗略地说,右值是临时对象(从函数返回),而左值是您也可以引用的对象。
例如:
X bar();
X x = bar();
在该代码中(忽略可能的编译器优化),创建了一个临时 X 对象来保存 foo 的返回值并传递给 x
的复制构造函数,这个临时对象是一个右值,仅存在于此语句中,不能被代码的其他部分引用。 x
不过是一个左值,你可以取回它的地址,以后再引用。
两种功能
void foo(X &);
void foo(const X &);
分别期望左值、非常量和常量。后者接受右值的原因是因为右值可以转换为对左值的常量引用。也就是说,你可以这样做:
const X &ref = bar();
这里 bar() 返回的临时变量直到 ref
超出范围才被销毁,因此它的生命周期已经延长到语句之外。但是你不能对非常量引用做同样的事情,
X &ref = bar(); // Error!
这是不允许的,因为 bar 的返回值是临时值(右值),无法修改。
但是请注意,这不是 C++11 的功能,它已经存在了。新功能是能够指定右值引用
void foo(X &&);
void foo(const X &&);
引入了更微妙的情况,例如 Universal References in C++11 - Scott Meyers。