Search code examples
c++gccgcc-warning

GCC 14 "possibly dangling reference to a temporary" warning or not, depending on the function argument


In the following C++ code GCC 14 with -Wall -Wextra flags produces a possibly dangling reference warning for function g(), but not f(). Why?

/* getval.h */
#include <string>
std::string const & getVal(std::string const & key);
/* main.cpp */
#include "getval.h"
#include <iostream>

void f()
{
  std::string key{"x"};
  std::string const &s = getVal(key);
  std::cout << "x->" << s << '\n';
}

void g()
{
  std::string const &s = getVal("x");
  std::cout << "x->" << s << '\n';
}

int main()
{
  f();
  g();
}
/* getval.cpp */
#include "getval.h"
#include <map>

std::string const & getVal(std::string const & key)
{
  static std::map<std::string, std::string> map = [](){
     std::map<std::string, std::string> m;
     m.emplace("x", "123");
     m.emplace("y", "456");
     return m;
  }();
  static std::string empty;

  auto it = map.find(key);
  return it != map.end() ? it->second : empty; 
}

/* Output:

/app/main.cpp: In function 'void g()':
/app/main.cpp:13:22: warning: possibly dangling reference to a temporary [-Wdangling-reference]
   13 |   std::string const &s = getVal("x");
      |                      ^
/app/main.cpp:13:32: note: the temporary was destroyed at the end of the full expression 'getVal(std::__cxx11::basic_string<char>(((const char*)"x"), std::allocator<char>()))'
   13 |   std::string const &s = getVal("x");
      |                          ~~~~~~^~~~~
Program returned: 0
x->123
x->123

I would expect it to produce the warning in both cases or none (earlier gcc versions or clang do not produce a warning).


Solution

  • This is known gcc issue. And false positive.

    You can

    • Disable warning with -Wno-dangling-reference or drop -Wextra.
    • or modernize your code with use of std::string_view (this is just like a fancy version of strings const reference):
    [[nodiscard]] std::string_view getVal(std::string_view key)
    {
      using namespace std::literals;
      static const std::map<std::string_view, std::string_view> map {
         { "x"sv, "123"sv },
         { "y"sv, "456"sv },
      };
      auto it = map.find(key);
      return it != map.end() ? it->second : ""sv; 
    }
    

    https://godbolt.org/z/P4nxh8nYa

    Edit:
    Since you prefer use #pragma GCC diagnostic and you did it incorrectly, here is better way to do it:

    // check if problematic warning -Wdangling-reference is available
    #if __GNUC__ >= 13
    #define GCC_PUSH_DIAGNOSTIC_DISABLE_DANGLING_REF _Pragma("GCC diagnostic push") \
        _Pragma("GCC diagnostic ignored \"-Wdangling-reference\"")
    
    #define GCC_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
    #else
    #define GCC_PUSH_DIAGNOSTIC_DISABLE_DANGLING_REF 
    #define GCC_DIAGNOSTIC_POP 
    #endif
    
    [[nodiscard]] std::string const& getVal(std::string const& key);
    
    void f()
    {
        std::string key { "x" };
        std::string const& s = getVal(key);
        std::cout << "x->" << s << '\n';
    }
    
    void g()
    {
        GCC_PUSH_DIAGNOSTIC_DISABLE_DANGLING_REF
        std::string const& s = getVal("x");
        GCC_DIAGNOSTIC_POP
    
        std::cout << "x->" << s << '\n';
    }
    

    Live demo