我试图理解为什么第二段代码编译得很好,因为第一段代码没有。
int & test(void) {
int v = 0;
return v;
}
int main(void){
int & r = test();
return 0;
}
我知道这不起作用,因为您无法将引用传递给将被删除的自动变量。在我看来,下面的代码应该有同样的问题,但它没有。
int & test1(int & x) {
return x;
}
int & test2(void) {
int x = 0;
return test1(x);
}
int main(void){
int & r = test2();
return 0;
}
似乎中间函数正在解决问题。但为什么呢?
答案 0 :(得分:6)
两个"替代品" 都遭受同样的问题; main 中的r
是一个悬空引用,它所引用的内容早已不复存在,使用它会导致未定义的行为。
第一段摘录
在第一个例子中,编译器很容易看到你将一个引用返回给一个局部变量,这个(就像编译器知道的那样)并没有感觉..当引用到达 main 时,引用的实例将会死亡。
编译器是一个很好的冠军,并告诉你这个问题。
第二段摘录
在第二个示例中,您正在执行相同的操作,但在中间添加重定向。编译器有很多技巧,但是回溯每个可能的执行路径,看看开发人员是否通过间接返回对局部变量的引用,并不是其中之一。
编译器无法看到你是坏人,也无法就你不了解的问题发出警告。
无论你怎么做,都返回对局部变量的引用是不好的。
答案 1 :(得分:3)
考虑一下编译器必须做些什么来捕捉你正在演示的问题。它必须查看test1的所有调用者,看看他们是否将它传递给本地。也许这很容易,但如果插入越来越多的中间函数会怎么样?
int & test1(int & x) {
return x;
}
int & test2(int & x) {
return test1(x);
}
int & test3() {
int x = 0;
return test2(x);
}
int main(void){
int & r = test3();
return r;
}
编译器不仅要查看test1的所有调用者,还要查看test2的所有调用者。它还必须通过test2(想象它比这里的例子更复杂)来查看它是否正在将任何自己的本地传递给test1。将其推断为一段真正复杂的代码 - 跟踪这类事情会非常复杂。编译器只能做很多事情来保护我们自己。
答案 2 :(得分:1)
这两个代码示例格式错误并且具有未定义的行为,因为退出函数后将删除本地对象。所以引用无效。
要理解第二个示例与第一个示例没有区别,您可以通过以下方式重写它(调用第二个函数的插入内容)
/*
int & test1(int & x) {
return x;
}
*/
int & test2(void) {
int x = 0;
/* return test1(x);*/
int &r = x;
return r;
}
如您所见,示例之间没有任何区别。
要达到你想要的效果,你可以采取以下方式
int test()
{
int v = 0;
return v;
}
int main()
{
const int & r = test();
return 0;
}