1

I have read a few posts on SO about prvalue and guaranteed copy elision (especially this one). But I am still confused about how prvalue works.

Consider the following code (C++17):

class Y
{
public:
    Y() { cout << "Y dctor\n"; }
};

class X
{
public:
    X() { cout << "X dctor\n"; }
    X(const X&) { cout << "copy X\n"; }
    X(const Y&) { cout << "copy Y\n"; }
};

X fun()
{
    Y y;
    return X(y);
}

int main()
{
    X x1 = fun();
}

In my current understanding, X(y) is a prvalue, and this prvalue is used to initialize the return value of fun(), which is also a prvalue (so the prvalue is "copied and passed down"). And this (returned) prvalue is then used to initialize the variable x1 by calling X(y) to construct x1. There is no temporary created or object copied in this process, only prvalue (which is not object in this circumstance) passing.

However, my question is, at the point when the return value of fun() has just been initialized by the prvalue of X(y), the call of fun() should have been finished, and thus the local variable y should have been destroyed. But then, how could x1 be constructed from the returned prvalue by calling X(y) without the object of y?


A follow-up question is about when we return named variable. Consider two more functions:

X g()
{
    X x;
    return x;
}

X h()
{
    return g();
}

int main()
{
    X x2 = h();
}

From the answer I linked above, it is said that:

... nothing changes for named return value optimization (NRVO).

So does this mean what happens above in C++14 happens exactly the same in C++17 for each implementation?

Since I don't understand prvalue well, I don't understand how exactly the compiler is dealing with the above in C++17. I know that eventually x will be copied directly to x2 (and we need an accessible copy/move constructor for that to happen), but to me, after the point return g();, X x2 = h(); looks similar enough to X x1 = fun(); (as g() and h() are all prvalues). So I wonder if the concept of prvalue in C++17 will make the internal process different from what happens internally in C++14 for the above code?

CPPL
  • 726
  • 1
  • 10
  • "*So does this mean what happens above in C++14 happens exactly the same in C++17 for each implementation?*" No. Some of those return values are named, and some of them *aren't*. `g` uses NRVO. `h` uses guaranteed elision. – Nicol Bolas Jul 09 '22 at 03:34
  • @NicolBolas Thank you for pointing that out. Could you please explain a little bit more in details on how these two interact here? – CPPL Jul 09 '22 at 03:43
  • This isn't that complicated. `h` interacts with `g` in the *exact same way* as it would with *any* function that returns a prvalue. `h` doesn't know anything about the internals of `g`; all it knows is its signature. – Nicol Bolas Jul 09 '22 at 04:01
  • @NicolBolas For the above code containing `g` and `h`, when I use release mode, I only got `X dctor` as the output. I think this is due to NRVO and it constructs `x2` directly from `X x;`. In debug mode, I got `X dctor` and `copy X` as outputs. To me, this is like constructing `x` locally, and then `return x;` copies the local `x` directly to `x2`. May I ask if my understandings are correct? And may I ask how NRVO and guaranteed elision are used in those processes? – CPPL Jul 09 '22 at 04:53
  • @NicolBolas To me, this is like using either NRVO (in release mode) or guaranteed elision (in debug mode), but not both... Thank you very much for your patience, I'm quite new to those optimization/elision features... – CPPL Jul 09 '22 at 05:07

0 Answers0