为什么作为返回类型的右值引用不能初始化非常量引用?
Why rvalue reference as return type can't be initialization of non-const reference?
我读了这个问题,我知道右值引用是一个左值。
但是,对于此代码(示例 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;
它编译并告诉我们右值引用可以绑定到左值引用。
现在,这两个问题呢:
- 在示例 1 中,
fun()
是右值吗? - 在示例 1 中,
fun()
是右值引用吗?
示例 3 告诉我们fun()
是右值引用,示例 4 告诉我们右值引用可以绑定到左值引用(常量和非常量)。那为什么示例 1 中的fun()
不能绑定到左值引用呢?
示例4 还指示右值引用是左值,但示例 1 中的编译错误告诉我们,那里fun()
(在示例 3 中被证明是右值引用)是右值。那么,右值引用是右值还是右值?
如果原因是fun()
只是一个表达式,它暂时存在并且会立即死亡,为什么示例 2 中的fun()
不被视为右值,而它也只是一个没有名称的表达式?返回左值引用的函数的函数表达式和右值引用之间有什么区别?
我知道右值引用是一个左值。
你说的是两个不同的东西:类型和值类别。
int&& ri = 0; // ri's type is rvalue reference (to int)
// ri's value category is lvalue; it's a named variable.
给定您的第一个样本,fun()
返回的是一个 x值,它属于右值。
以下表达式是 xvalue 表达式:
- 函数调用或重载运算符表达式,其返回类型是对对象的 rvalue 引用,例如
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
在第三个样本中,
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
首先,此代码表现出未定义的行为:
int && fun(){
return 1;
}
在这里,您返回的是对1
的悬空引用,这超出了范围。
引用如何成为右值?
为了理解这一点,最好不要将引用视为指针的另一种语法,而是将其视为某些已存在对象的另一个名称。
然后最好回顾一下引用初始化规则:
第一个引用初始化规则指出,引用可以初始化("绑定")为与引用兼容的值。这意味着
int&
可以绑定到int&
int&&
可以绑定到int&&
const int&
可以绑定到int&
在这种情况下,不会检索右侧的实际引用值,而是直接绑定到新引用。 请注意,int&
与int&&
不兼容,这些是不同的类型。
第二个引用初始化规则指出const
左值引用(const int&
)和右值引用(int&&
)可以绑定到:
- x值或普尔值
- 作为其他任何事情的最后手段
在后者的情况下,引用绑定到表达式的结果。在const int& x = fun()
的情况下,调用fun()
的结果将首先被"物化"(检索),然后其值将绑定到引用。
但要做到这一点,必须const
左值引用。这就是为什么错误指出非const
int&
不能绑定到int
,因为int
是评估fun()
的结果。
非常量引用不能绑定到右值,就这么简单。
int & a=fun();
不起作用,因为a
是非常量引用,而fun()
是右值表达式。
在第二种情况下,fun()
返回一个非常量左值引用,当然,它可以绑定到另一个非常量引用。
decltype(fun()) b=1;
之所以有效decltype(fun())
int &&
,因此可以绑定到整数文字1
。
在示例 1 中,
fun()
是右值吗?
是的。
在示例 2 中,
fun()
是右值引用吗?
不,这是一个左值引用。
示例 3 告诉我们
fun()
是一个右值引用,示例 4 告诉我们一个右值引用可以绑定到左值引用(常量和 非恒量)。那为什么示例 1 中的fun()
不能绑定到左值 参考?
因为该函数fun
返回右值引用,但fun()
本身是右值表达式。fun()
是一个右值。
示例 4 还指示右值引用是左值,但 示例 1 中的编译错误告诉我们
fun()
那里,这被证明是 示例 3 中的右值引用是右值。所以,是一个右值引用左值 还是右值?
右值引用是左值。
如果原因是
fun()
只是一个表达式,则存在 暂时并且会立即死亡,为什么示例 2 中的fun()
不是 被视为右值,而它也只是一个表达式,没有 一个名字?返回左值引用的函数的函数表达式和右值引用之间有什么区别?
因为在示例 2 中,fun()
是一个左值。从 N4296, §3.10/1.1:
[...]调用返回类型为左值的函数的结果 引用是一个左值。
关于示例 2 中收到的警告,您应该显示确切的消息。不过,这可能只是因为您返回了对局部变量的引用。局部变量的生存期有限,因此引用它们超过其生存期是未定义的行为,因此发出警告。
关键是表达式的值类别不仅取决于其类型,例如
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
此外,正如 rustyx 在他的回答中指出的那样,函数定义
int && fun(){
return 1;
}
可能会导致未定义的行为,因为临时对象将在执行 return 语句后立即销毁。
我认为您正在混合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;
}
- 取消引用运算符不能重载
- 为什么我不能将引用作为 std::async 的函数参数传递
- 类型为 "Bucket&"(未限定的 const 限定)的引用不能使用 "SortedList." 类型的值进行初始化 如何修复此错误?
- 为什么我不能引用指向实例化对象的函数的指针?
- 为什么不能直接引用作用域枚举类成员,而不能为无作用域枚举生成类成员?
- 为什么我不能返回带有透明函子的法线映射引用?
- 为什么我不能在运算符=中使用引用类型?
- 为什么我不能直接从返回指针的函数返回对指针的引用?
- 矢量迭代器不能与 std::shared_ptr<> 取消引用
- std::remove() 按预期处理文字,但不能与取消引用的迭代器一起工作
- 为什么转发声明的好友类不能在类中引用?
- C++不能取消引用结束列表迭代器
- 为什么右值不能绑定到非常量左值引用,除了写入临时无效的事实?
- 为什么引用不能与编译时函数一起使用?
- 为什么作为返回类型的右值引用不能初始化非常量引用?
- C++字符串引用不能共存
- 对类型 'A *' 的非常量左值引用不能绑定到不相关的类型 'std::shared_ptr<A>' 的值
- 为什么常量引用不能延长通过函数传递的临时对象的生存期?
- 对抽象类的引用不能传递给线程函数?
- C++错误:类型的引用不能用类型的值初始化