Alex Goft
Alex Goft

Reputation: 1124

static reference referring to temporary variable

#include <iostream>

int getID ( int k ) {
    static int& r = k;
    return r++;
}

int main()
{
    int a = getID ( 10 );
    int b = getID ( 10 );
    std::cout << "a = " << a << ", b = " << b  << std::endl;
    return 0;
}

I don't understand why this code compiles.

  1. How is it possible for a static reference to refer to a local variable k, which will disappear in the end of the function call).
  2. On the second call we reinitialize the static reference with a new variable. Please explain what happens here, how come static REFERENCE can be "redefined" (i guess that i don't understand the meaning of a reference to a static variable inside a method).

Upvotes: 4

Views: 2906

Answers (2)

Chris Beck
Chris Beck

Reputation: 16204

  1. The language does not prevent you from binding references to objects of limited lifespan. That would make it very hard to use references.

    (It does prevent you from binding references to temporaries. But a parameter to a function is an l-value, like a local variable, so it's allowed.)

    But, if you write code where a reference binds to an object that it outlives, and you keep using the reference, you get undefined behavior, no diagnostic required, and most likely a segfault. It's essentially the same as a dangling pointer in C.

  2. On the second call, you do not rebind the static reference variable to the new temporary. The static variable is only initialized once, the first time the function is called. That line is effectively skipped on all subsequent calls.

If you want a language that would catch mistakes like this for you, and make sure you don't get bitten by dangling references, you could look at Rust. The rust compiler has a "borrow checker" which will check your references for you, without imposing run-time overhead like the many well-known garbage-collected languages will. However Rust does not have static variables so there's no direct translation of that code ;)

In C++, I guess that the above mistake would likely be caught by a static analysis tool like Coverity, which will do some lifetime checking in your code when it scans it. But the C++ compiler won't do it for you, you would need to use a 3rd party tool.

Upvotes: 6

Barry
Barry

Reputation: 302817

How is it possible for a static reference to refer to a local variable k, which will disappear in the end of the function call).

It's totally possible. You just end up with a dangling reference, which leads to undefined behavior. But the static part isn't significant here.

On the second call we reinitialize the static reference with a new variable. [...]

No, we don't. A static variable is initialized only once. So the second time in getID(), we're still referring to the previous temporary k. Which we then increment, which is undefined behavior. One facet of "undefined behavior" is "code that looks like it works."

Consider a new type:

struct WrappedInt {
    ~WrappedInt() {
        i = 0;
    }

    int i;
};

and rewrite your code to use it instead:

int getID ( WrappedInt k ) {
    static WrappedInt& r = k;
    return r.i++;
}

Here, we will see that once the temporary goes out of scope, and it explicitly zeroes out its value, when we reread it (with our dangling reference), we will get back 0:

int main()
{
    int a = getID ( WrappedInt{10} );  // a == 10
    int b = getID ( WrappedInt{10} );  // b == 0 
    std::cout << "a = " << a << ", b = " << b  << std::endl;
    return 0;
}

Upvotes: 3

Related Questions