Search code examples
c++referenceglobalrvalueobject-lifetime

Reference to anonymous rvalue is corrupted


Why does the following code...

#include <iostream>
#include <map>

template< typename T, typename U >
class Map
{
public:
  Map( const T& t, const U& u ) { map_[ t ] = u; }
  Map< T, U >& operator() ( const T& t, const U& u )
  {
    map_[ t ] = u;
    return *this;
  }
  U& operator[] ( const T& t) { return map_[ t ]; } 

private:
  std::map< T, U > map_;
};

Map< int, std::string >& g_map = Map< int, std::string> ( 1, "lorem" )
                                                        ( 3, "ipsum" )
                                                        ( 5, "dolor" );

int main( int argc, char* argv[] )
{
  std::cout << g_map[3] << std::endl;
  return 0;
}

...produce this corrupted output?...

>g++ -g main.cpp
>./a.out
ipsumÿÿÿÿlorem!h€Ap€AD€A!ˆ€A¼gì¿P€A€A,€A!p€A€AY

I learned recently that assigning a reference to an anonymous rvalue extends the rvalue object's lifetime. So I thought that since the anonymous rvalue std::map is referred to by global-scope g_map, its lifetime would have been extended to that of a global-scope variable and it would have been valid to use g_map as any other global variable (where if not for the reference, the anonymous rvalue would have died at the closing semicolon).

Can someone please explain how lifetime extension rules apply to the above?

Compilation performed with gcc 4.9.2.


Solution

  • You essentially have this:

    class C {
    public:
      C& detemporize() { return *this; }
    };
    
    C& cr = C().detemporize();
    

    A temporary C instance is created. Then a method is called on it, which returns a C& reference. The compiler doesn't know nor care that the return value refers to that same temporary; for all it knows, it might very well return a reference to some global, long-lived object.

    Anyway, cr ends up referring to that temporary, which then promptly dies, leaving cr dangling. Any subsequent attempt to use it exhibits undefined behavior.

    In your code, Map::operator() plays the role of detemporize(), leaving g_map a dangling reference.