5

Having a class holding a reference I would expect the following code to fail miserable, but it compiles:

#include <iostream>

struct ReferenceHolder
{
    std::string& str;

    ReferenceHolder(std::string& str)
    :   str(str)
    {}
};

// Why does this compile?
ReferenceHolder f() {
    std::string str = "Hello";
    return ReferenceHolder(str);
}

int main() {
    ReferenceHolder h  = f();
    std::cout << "Should be garbage: " << h.str << '\n';
    return  0;
}

Compiler: g++ 4.7.2 (with -std=c++11)

Edit: Even with -fno-elide-constructors it compiles happily

  • Apparently it's being moved. It doesn't compile if you `delete` the move constructor explicitly. – jrok Apr 06 '14 at 11:56
  • @jrok True, but as far as I can see it's both copyable and movable. – dyp Apr 06 '14 at 11:57
  • Interestingly, according to the presentation of the Mill CPU (Security talk), on this architecture you would get a segmentation fault. – Matthieu M. Apr 06 '14 at 12:07

2 Answers2

7

There's no problem with copy-initialising your class, as your example does: the new reference is simply initialised to refer to the same object as the old one. Of course, you then get undefined behaviour when the function return leaves the reference dangling.

The reference prevents default-initialisation and copy-assignment; so the following small change will fail for those reasons:

ReferenceHolder h;  // ERROR: can't default-initialise the reference
h = f();            // ERROR: can't reassign the reference.
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

This code has undefined behaviour, see the classic answer from Eric Lippert.

The the reference in h is bound to the reference in the return value from f(), which is bound to the ReferenceHolder in the return expression, and so h references the str from f() outside its scope.

Community
  • 1
  • 1
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • I know that. See : "Should be garbage" –  Apr 06 '14 at 12:01
  • 1
    @DieterLücking: Undefined behaviour doesn't mean "garbage", it means "anything". – Mankarse Apr 06 '14 at 12:06
  • 1
    In other words, the default copy constructor is just recursively copying members (as it always does), and for references, this just means referencing the original data. The only thing left to address is your expectation that the code will fail miserably and that the string will contain garbage, when undefined behaviour doesn't guarantee either of those things. – Mankarse Apr 06 '14 at 12:14
  • So this would all be OK if `str` inside `f()` had static storage duration? (without move constructor) – M.M Apr 06 '14 at 14:49