我读了this个问题,我知道右值参考是一个左值。
但是,对于此代码,示例1,
int &&fun() {
return 1;
}
int main() {
int &a = fun();
}
编译时:
error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
所以C ++编译器告诉我fun
的返回类型是一个右值。
右值参考如何成为右值?
我认为编译器应该以同样的方式处理左值引用和右值引用,但是这段代码,例2,
int & fun(){
int b;
return b;
}
int main(){
int & a=fun();
}
可以编译(尽管如此,我收到警告)。
我想可能fun
的返回类型在某些时候发生了变化。
尝试编译示例3:
int &&fun() {
return 1;
}
int main() {
decltype(fun()) b = 1;
}
它成功编译。所以我可以说fun
的返回类型实际上是一个右值引用。
那么,为什么右值参考变成右值?
以下是示例4:
int &&a = 1;
int &b = a;
它编译并告诉我们一个右值引用可以绑定到左值引用。
现在,那两个问题呢:
fun()
是否是左值?fun()
是左值参考吗?示例3告诉我们fun()
是一个右值引用,示例4告诉我们一个右值引用可以绑定到左值引用(const和非const)。那么为什么不能将示例1中的fun()
绑定到左值引用?
示例4还表明右值引用是左值,但示例1中的编译错误告诉我们fun()
,其中证明是示例3中的右值引用,是一个右值。那么,是左值参考左值还是左值?
如果原因是fun()
只是一个临时存在并且会立即死亡的表达式,为什么示例2中的fun()
不被视为右值,而它也只是一个表达式一个名字?返回左值引用的函数的函数表达式与右值引用之间有什么区别?
答案 0 :(得分:3)
我知道右值参考是一个左值。
你在谈论两件不同的事情:打字和value category。 e.g。
int&& ri = 0; // ri's type is rvalue reference (to int)
// ri's value category is lvalue; it's a named variable.
鉴于您的第一个样本,fun()
返回的是一个xvalue,它属于rvalues。
以下表达式是xvalue表达式:
- 函数调用或重载的运算符表达式,其返回类型是对象的右值引用,例如
std::move(x)
;
然后,
int &a = fun(); // fails; lvalue-reference can't bind to rvalue
在第二个样本中,fun()
返回的是左值,
以下表达式是左值表达式:
- 函数调用或重载的运算符表达式,其返回类型为左值引用,例如
std::getline(std::cin, str)
,std::cout << 1
,str1 = str2
或++it
;
然后
int & a=fun(); // fine; lvalue-reference could bind to lvalue
在第3个样本中,
decltype(fun()) b = 1; // the return type of fun() is rvalue-reference;
// this has nothing to do with the value category of its return value
// b's type is rvalue-reference too, btw its value category is lvalue
在第4个样本中,
int &&a = 1; // fine; rvalue-reference could bind to rvalue
// a's type is rvalue-reference, its value category is lvalue
int &b = a; // fine; lvalue-reference could bind to lvalue
// b's type is lvalue-reference, its value category is lvalue
答案 1 :(得分:1)
非const引用不能绑定到rvalues,就像那样简单。
int & a=fun();
不起作用,因为a
是非const引用,而fun()
是右值表达式。
在第二种情况下,fun()
返回一个非const左值引用,当然可以绑定到另一个非const引用。
decltype(fun()) b=1;
有效,因为decltype(fun())
是int &&
,因此可以绑定到整数文字1
。
在示例1中,
fun()
是否是左值?
是
在示例2中,
fun()
是左值参考吗?
不,这是一个左值参考。
示例3告诉我们
fun()
是右值引用,示例4告诉我们右值 引用可以绑定到左值引用(const和 非const)。那么为什么不能将示例1中的fun()
绑定到左值 参考
因为函数fun
返回右值引用,但fun()
本身是右值表达式。 fun()
是一个右值。
示例4还表明右值引用是左值,但是 示例1中的编译错误告诉我们
fun()
那里,证明是 示例3中的右值引用是右值。所以,是一个右值参考左值 还是rvalue?
右值参考是左值。
如果原因是
fun()
只是一个存在的表达式 暂时会立即死亡,为什么示例2中的fun()
不是 认为是一个右值,而它也只是一个没有的表达 一个名字?返回左值引用的函数的函数表达式与右值引用之间有什么区别?
因为在示例2中,fun()
是左值。从N4296,§3.10/ 1.1:
[...]调用返回类型为左值的函数的结果 引用是左值。
关于例如2的警告,您应该显示确切的消息。这可能只是因为你返回对局部变量的引用。局部变量的生命周期有限,因此在它们的生命周期之外引用它们是未定义的行为,因此是警告。
答案 2 :(得分:1)
首先,此代码显示未定义的行为:
int && fun(){
return 1;
}
在这里,您将返回对1
的悬空引用,该引用超出了范围。
右值参考如何成为左值?
为了理解这一点,最好不要将引用视为指针的另一种语法,而是将某些已存在的对象视为另一个名称。
然后讨论参考初始化规则是好的:
first reference initialization rule表示可以将引用初始化(“绑定”)到引用兼容的值。这意味着
int&
可以绑定到int&
int&&
可以绑定到int&&
const int&
可以绑定到int&
在这种情况下,不会检索右侧的实际引用值,而是直接绑定到新引用。
请注意,int&
与int&&
不兼容,这些是不同的类型。
second reference initialization rule表示const
左值参考(const int&
)和左值参考(int&&
)可能绑定到:
如果是后者,则引用将绑定到表达式的结果。对于const int& x = fun()
,调用fun()
的结果将首先“实现”(检索),然后其值将绑定到引用。
但要实现这一点,左值引用必须为const
。这就是为什么错误表明非const
int&
无法绑定到int
,因为int
是评估fun()
的结果。
答案 3 :(得分:1)
关键是表达式的值类别不仅取决于其类型,例如
int&& a = 1;
int&& fun();
// int&& ri = a; // ill-formed, the expression a is of type int&&, but is an lvalue
int&& ri = fun(); // ok, the expression fun() is of type int&&, and is also an rvalue
另外,正如在his answer中指出的rustx,函数定义
int && fun(){
return 1;
}
可能会导致未定义的行为,因为临时对象将在执行return语句后立即销毁。
答案 4 :(得分:0)
我认为你正在混合rvalue
和rvalue reference
。在你的第一个例子中
int && fun(){
// 1 is an rvalue so it can be bound to an rvalue reference
// this will produce undefined behavior though because you
// a returning a dangling reference to an temporary that will
// go out of scope at the end of this function
return 1;
}
int main(){
// you are trying to take a reference to a temporary object.
// this is (deliberately) not valid
int & a=fun();
// One legal way of doing this is by declaring your reference const:
const int& b = fun();
// because this extends the lifetime of the temporary object returned
// by fun() to match the lifetime of the reference.
}
在你的第二个例子中:
int & fun(){
// you have allocated an new int in the free store so the
// lifetime of this int is until the main exits. The return
// type here is an lvalue that can be safely bound to an
// lvalue reference
return *(new int);
}
int main(){
// binding lvalue reference to lvalue this is ok
int & a=fun();
}
在你的第三个例子中
int && fun(){
// 1 is an rvalue and can be bound to an rvalue reference
return 1;
}
int main(){
// decltype(fun()) is equal to int&& so it's ok to bind
// an rvalue reference to an rvalue
decltype(fun()) b=1;
}