0

Given the code below, it will give a warning:

warning: a temporary bound to 'Foo::b' only persists until the constructor exits [-Wextra]

struct Bar {
    inline void print() const { std::cout << "Bar" << std::endl; }
};

struct Foo {
    Foo() : b{} {}                    // create temporary
    void print() const { b.print(); } // OK but I'm expecting a crash here
    Bar&& b;
};

Foo f;     // Expecting lifetime of temporary ends here
f.print(); // OK - prints "Bar"

I am expecting accessing Bar::print() via Foo::b will lead to crash after Foo's constructor exits.

How come the program still calls Bar::print() without a crash?

Joseph D.
  • 11,804
  • 3
  • 34
  • 67
  • 2
    It's undefined behavior. This even may mean, it works or seems to work. – Scheff's Cat Apr 25 '18 at 05:44
  • You've just proved by evidence that undefined can be unexpected. – bipll Apr 25 '18 at 05:46
  • is `Bar&& b;` a dangling pointer? – Joseph D. Apr 25 '18 at 05:47
  • 1
    According to [SO: C++ Double Address Operator? (&&)](https://stackoverflow.com/a/4549167/7478597) a dangling rvalue reference. ;-) – Scheff's Cat Apr 25 '18 at 05:49
  • @Scheff However, references can't be null right? how come it can be dangling? – Joseph D. Apr 25 '18 at 05:59
  • Since `Bar` is not polymorphic and has no data members, there is nothing for the reference to point to, dangling or not. Why should the compiler create a distinction between a valid reference to nothing and a dangling reference to nothing? It's not obliged to create a crash just because it could. – TrentP Apr 25 '18 at 05:59
  • @TrentP, can you refer me to the part of the specs that states that? – Joseph D. Apr 25 '18 at 06:00
  • @codekaizer - This question has been mulled over and over and over on SO. Changing "pointer" to "reference" and hanging on to the difference between the two, doesn't make it a whole new question. It's been answered to death. – StoryTeller - Unslander Monica Apr 25 '18 at 06:00
  • I resembled your sample in VS2013 knowing that any destroyed instance is filled with test patterns. For my surprise, I got similar warnings and it worked without crash in my case too. So, I realized on the 2nd glance that `Bar::print()` does not access any member. For all that, it is undefined behavior. – Scheff's Cat Apr 25 '18 at 06:01
  • A "dangling" reference is really easy to build: `int *p = nullptr, &r = *p;` `r` is a reference to a.. (how should I call this?) ...non-existing instance. – Scheff's Cat Apr 25 '18 at 06:02
  • @StoryTeller, I understand. I have just encountered this "dangling rvalue reference" today. not insisting it's a new question. just seeking clarifications. – Joseph D. Apr 25 '18 at 06:02
  • Ultimately, yes a reference can dangle. More generally, any glvalue expression can end up not referring to a valid object because of human error. Once that happens, there's plenty of UB to choose from. – StoryTeller - Unslander Monica Apr 25 '18 at 06:04
  • @StoryTeller, thank you. so based on your statement, is it safe to say that references can be null too? – Joseph D. Apr 25 '18 at 06:05
  • 1
    IMHO, a reference should grant a "living" object (in opposition to a pointer which might be intentionally a `nullptr`). But it can happen as well as division by 0 (which probably is rarely done intentionally). – Scheff's Cat Apr 25 '18 at 06:07
  • 1
    Yes and no. Formally references aren't pointers, and must be initialized with the identity of an object. But... like Scheff shows, you can break it by introducing UB elsewhere (say with a null pointer). – StoryTeller - Unslander Monica Apr 25 '18 at 06:07
  • @StoryTeller, Scheff, thank you. added knowledge for today. – Joseph D. Apr 25 '18 at 06:09
  • @codekaizer, that a class with no data members and no virtual methods will have no data associated with it? If it had data, then what would it be? While your code triggers undefined behavior, compilers do not produce their output at random. There no reason to expect that a dangling reference to nothing should crash, absent a debugging option specifically meant to detect such a thing. Precisely what is created with an object is not specified, but something like §9.5.1, about what can be in a class in a union, gives an idea of what is expected of a compiler. – TrentP Apr 25 '18 at 06:21
  • @TrentP - "Since `Bar` is not polymorphic", if it's polymorphic, does it make a difference? – Joseph D. Apr 25 '18 at 06:34
  • @codekaizer, yes. A polymorphic class will have a some sort of pointer into a table of function pointers, to allow the compiler to know what virtual methods to call from a base class pointer. It's why that "no polymorphic classes in a union" rule is there. If `Bar::print()` were a virtual method one could create code that would likely crash calling a dangling reference. – TrentP Apr 25 '18 at 06:44
  • @TrentP, that's something. thank you! – Joseph D. Apr 25 '18 at 06:46

0 Answers0