0

Why does this code work? It prints out 60 every single time. First of all, const thingy& indicates that the function returns a reference to an already existing variable, not a nameless construction. Secondly, shouldn't that temporary die when the function returns, thus creating a null reference? I am using whatever the latest GCC version is on OSX... Can somebody please explain to me why this works?

#include <iostream>

using namespace std;

struct thingy {
    double things;
    double moreThings;
    double evenMoreThings;
};

const thingy& getThingy() {
    return {60, 60, 60};
}

int main() {
    cout << getThingy().evenMoreThings << endl;
}

And if that works, then why doesn't this?

const thingy& getThingy() {
    thingy t{60, 60, 60};
    return t;
}
Sus Among Us
  • 103
  • 2
  • 11
  • 2
    Both of these are undefined behavior, so anything goes, including appearing to work. – GManNickG Jun 17 '16 at 03:08
  • @GManNickG The first example appears to work every single time. I even put it in a while loop that breaks if it doesn't, and it works fine on multiple trials... If you read the accepted answer I guess that is whats going on; compiler silliness, which means this technically shouldn't be happening...? – Sus Among Us Jun 17 '16 at 03:18
  • 2
    Appearing to work every single time is a perfectly valid behavior for undefined behavior. It will stop working the day you demo your product. – GManNickG Jun 17 '16 at 03:22
  • Okay thank you very much! I will keep that in mind for the future. In the past I have let things slip that shouldn't have worked, but did under rigorous testing. I suppose I'm not taking into account other platforms, IDEs, debug vs release mode, so thank you! – Sus Among Us Jun 17 '16 at 03:24
  • 1
    FYI my deleted answer was wrong because it doesn't apply in all cases (like here). – chris Jun 17 '16 at 03:27

3 Answers3

2

Neither of your two options are required to work by the C++ standards, and so it shouldn't be assumed that either will necessarily work under any particular circumstances.

Compiler specifics have caused the first option to work where you return the value directly.

Ideally, you would return by value (thingy getThingy();) instead to ensure compatibility. Any compiler worth your time will still apply return value optimisation to this, preventing the need for a copy constructor call and still allowing the necessary efficiency.

Mitch
  • 1,839
  • 16
  • 23
  • Thank you. Does this mean that, except situations in which RVO cannot occur, there won't be any performance issues? – Sus Among Us Jun 17 '16 at 03:21
  • 1
    RVO is thankfully a well-implemented feature of all major compilers. I tend to take it for granted for the most part, and will do some actual debugging through in instances where I really want to make sure it is coming into play, by putting breakpoints in my copy constructor. – Mitch Jun 17 '16 at 03:28
1

The compiler is performing the Return Value Optimization here.

https://en.wikipedia.org/wiki/Return_value_optimization

The compiler is able to take the value constructed at the return and won't even need to copy it. However, in the example where you construct the struct inside the function, it really is a local variable, and thus falls out of scope at the end of the function, invalidating the reference.

Michael S
  • 186
  • 2
  • 7
  • May I ask, then, why this doesn't work? `return thingy(t);` I am not very knowledgeable about these things, but I believe that this creates a temporary and binds it to the const reference, as with `return {60, 60, 60};` – Sus Among Us Jun 17 '16 at 03:11
  • 2
    @Naerion: None of these "work". RVO may explain this particular quark, but you cannot return a reference to something that doesn't exist. Undefined behavior can sometimes look like it's working. – GManNickG Jun 17 '16 at 03:16
-1

We should not mix rvalue and lvalue references. The best option is to return by copy..

Returning lavalue rerense of local variable will always lead to problems.

skverma
  • 66
  • 4