https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115361
Bug ID: 115361 Summary: "possibly dangling reference to a temporary" when object is_empty Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: arthur.j.odwyer at gmail dot com Target Milestone: --- Bug #115229 is related. // https://godbolt.org/z/os63oEEax struct GetKey { int& rn( int& x) const { return x; } const int& rc(const int& x) const { return x; } int* pn( int& x) const { return &x; } const int* pc(const int& x) const { return &x; } static int& srn( int& x) { return x; } static const int& src(const int& x) { return x; } }; int f0(int t) { const int& x = GetKey().rn(t); return x; } // False positive int f1(int t) { int& x = GetKey().rn(t); return x; } // OK int f2(int t) { const int& x = GetKey().rc(t); return x; } // False positive int f3(char c) { const int& x = GetKey().rc(c); return x; } // True(*) positive int f4(int t) { const int* x = GetKey().pn(t); return *x; } // OK (compare f0) int f5(int t) { int* x = GetKey().pn(t); return *x; } // OK int f6(int t) { const int* x = GetKey().pc(t); return *x; } // OK (compare f2) int f7(char c) { const int* x = GetKey().pc(c); return *x; } // False negative (compare f3) int f8(int t) { const int& x = GetKey().srn(t); return x; } // OK (compare f0) int f9(char c) { const int& x = GetKey().src(c); return x; } // True(*) positive (compare f3) // f9 in 14.1 is a true(*) positive; it had been a false negative in 13.1. // f7 in 14.1 gives -Wuninitialized on the use of `x`, but fails to give -Wdangling-reference! GCC 14.1 complains--- <source>:12:29: warning: possibly dangling reference to a temporary [-Wdangling-reference] 12 | int f0(int t) { const int& x = GetKey().rn(t); return x; } // False positive | ^ <source>:12:44: note: the temporary was destroyed at the end of the full expression 'GetKey().GetKey::rn(t)' 12 | int f0(int t) { const int& x = GetKey().rn(t); return x; } // False positive | I notice that GCC 13.1 includes -Wdangling-reference in -Wall, but then 13.2 removed it from -Wall again, presumably because of all the false positives. But then in 14.1 it's back in -Wall causing trouble again! IMO it doesn't even belong in -Wextra right now; but I see how you have to ship it in order to find out how it works in practice. (Still, the answer is "it doesn't work great.") The warning message could be improved immediately by changing the phrase "the temporary was destroyed" to "a temporary of type 'GetKey' was destroyed". And then change "reference *to* a temporary" to "reference *into* a temporary," since obviously we have no reference to the 'GetKey' temporary anywhere in this code. Another reason to mention the type of the temporary is that the naïve programmer (me!) might assume that the compiler was giving the warning because of cases like `f3`, which legitimately does dangle; but then it makes no sense that `f7` is accepted quietly! The solution to that puzzle is that GCC doesn't care about the *actual dangling* in `f3` at all; the warning is essentially a false positive about the temporary of type `GetKey`; GCC doesn't even realize that we have a temporary of type `int`! By mentioning the type of the temporary, you'll help the programmer build the correct mental model (of what the compiler is diagnosing) more quickly, with less trial-and-error. --- The big problem with this diagnostic, for programmers, is that it's so encumbered with heuristics that it's not easily actionable. My coworkers discovered by trial and error that you can get around it by replacing our code's const int& x = GetKey()(t); with static constexpr GetKey g; const int& x = g(t); (here `GetKey` is a template policy parameter, basically something like `std::hash<int>`, with an `operator()` — which also means that we cannot work around the problem by making the function `static`, as shown in `src` above). But this is dumb. Another more generic solution — proposed only tongue-in-cheek — was // https://godbolt.org/z/ezE9KjKnP const int& x = (+[](const int& z) -> decltype(auto) { return GetKey()(z); })(t); --- Since the heuristic is concerned only with dangling references to *members of the GetKey object itself*, could you maybe add yet another heuristic to suppress the warning whenever the GetKey object is_empty?