1

I'm trying to understand why the second piece of code compiles fine, given that the first doesn't.

int & test(void) {
    int v = 0;
    return v;
}


int main(void){
    int & r = test();
    return 0;
}

I understand that this doesn't work because you can't pass a reference to an automatic variable that will be deleted. It seems to me that the code below should have the same problem but it doesn't.

int & test1(int & x) {
    return x;
}

int & test2(void) {
    int x = 0;
    return test1(x);
}

int main(void){
    int & r = test2();
    return 0;
}

Seems like the intermediate function is solving the problem. But why?

Gottfried
  • 2,019
  • 3
  • 15
  • 16
  • The intermediate function does not solve anything, it just hides information from the compiler, so it does not find the error. – Deduplicator Jun 04 '14 at 18:34
  • 5
    It *does* have the same problem, the problem being undefined behavior. The C++ standard, and your compiler's documentation, do not make *any* guarantees of any kind about either program's behavior. There might still be a discernible reason why one is printing 0 and the other is printing garbage, but it's meaningless for most purposes. –  Jun 04 '14 at 18:34
  • also read this: http://stackoverflow.com/questions/4643713/c-returning-reference-to-local-variable – Csq Jun 04 '14 at 18:37

3 Answers3

6

Just because something compiles, doesn't mean it works...

The two "alternatives" both suffer from the same exact problem; r, in main, is a dangling reference, what it refers to is long gone, and using it will lead to undefined behavior.


1st snippet

In the first example it's easy enough for the compiler to see that you are returning a reference to a local variable, which (as compilers know) doesn't make any sense.. the referred to instance will be dead when the reference reach main.

The compiler is being a good champ and tells you about the issue.

2nd snippet

In the second example you are doing the same thing, but adding a redirection in-between. A compiler got many tricks up its sleeve, but back-tracing every possible execution path to see if a developer is returning a reference to a local variable, by indirection, isn't one of them.

The compiler can't see that you are being bad, and it cannot warn you about issues it doesn't know about.


Conclusion

Returning a reference to a local variable is bad, no matter how you do it.

Community
  • 1
  • 1
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
3

Think about what the compiler would have to do to catch the problem you're demonstrating. It would have to look at all callers of test1 to see whether they're passing it a local. Perhaps easy enough, but what if you insert more and more intermediate functions?

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;
}

The compiler would have to look not only at all callers of test1, but then also all callers of test2. It would also have to work through test2 (imagine that it's more complex than the example here) to see whether it's passing any of its own locals to test1. Extrapolate that to a truly complex piece of code--keeping track of that sort of thing would be prohibitively complex. The compiler can only do so much to protect us from ourselves.

SaganRitual
  • 3,143
  • 2
  • 24
  • 40
1

The both code examples are ill-formed and have undefined behaviour because local objects will be deleted after exiting the functions. So the references will be invalid.

To understand that the second example does not differ from the first example you could rewrite it the following way (insetad of calling the second function)

/*
int & test1(int & x) {
    return x;
}
*/
int & test2(void) {
    int x = 0;
/*    return test1(x);*/
    int &r = x;
    return r;
}

As you see there is no any difference between the examples.

To achieve what you want you could the following way

int test() 
{
    int v = 0;
    return v;
}


int main()
{
    const int & r = test();
    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335