Search code examples
c++referencec++23

C++23 Dangling Reference Compilation Error


The following apparently results in a compilation error in C++23:

int& f(int& x) {
    int y = ++x;
    return y;
}

error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'

But this one doesn't seem to even give us a warning:

int& f(int x) {
    int &y = ++x;
    return y;
}

Is this so much more difficult to catch during the compilation phase?

Thanks.


Solution

  • Why the first example doesn't compile, unlike the second

    To understand the difference, one must understand why the code fails to compile in C++23. Namely, in the first example, y is move-eligible because it names an implicitly movable entity and appears in a return statement. Therefore:

    The expression is an xvalue if it is move-eligible (see below); an lvalue if the entity is a function, variable [...]

    - [expr.prim.id.unqual] p1

    An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.

    - [expr.prim.id.unqual] p4

    In the first example, the move-eligibility of y makes it an rvalue, and the returned reference cannot bind to it. In the second example, y does not name an implicitly movable entity, so it's considered to be an lvalue, and the reference binding succeeds.

    The fact that the first example doesn't compile is not because the C++ committee has made an effort to detect dangling references; instead, this is just a convenient side effect of some wording changes when it comes to implicit moves in return statements.

    Detecting dangling references in general

    Detecting dangling references outside of this one, trivial case is very difficult. It generally requires lifetime annotations for functions, similar to Rust. Some proposals are focused on detecting simple cases at least.

    For example, Herb Sutter has proposed standardizing analysis based on acyclic control flow graphs (ACFGs) in P1179: Lifetime safety: Preventing common dangling. This would at least make local analysis (within the same function) possible. The fact that this proposal is 47 pages long demonstrates that it's not a trivial issue though.

    More trivial cases can be caught with @BrianBi's proposal P2748: Disallow Binding a Returned Glvalue to a Temporary