-1

This question may not be useful in practice, but I would really like to understand how the compiler sees the code behind the scenes, so I asked...

Assume that I am using C++17 or newer. Consider the following code:

class X
{
public:
    X() {}
    explicit X(const X&) {}
};

int main()
{
    X x = X();
}

Since the copy elision is mandatory in C++17 and newer, and since the mandatory copy elision does not check for the accessibility of the copy/move constructor it omits, X x = X(); will always work: the explicitness of the copy constructor will be ignored and X() will be used to construct the object represented by x directly. However, this is not the case for C++14 or earlier: in those older versions, the explicitness of the copy constructor will stop X x = X(); even when the optimization for copy elision is on. Hence, I think there must be a difference between the implementations in pre-C++17 and in post-C++17 for this: for the former, the checking of the accessibility seems to happen logically before the copy elision, whereas for the latter, the copy elision seems to happen logically before the checking of the accessibility.

What I would like to ask is how the latter implementation might be done. How might the mandatory copy elision in C++17 and newer be implemented so that the compiler knows where to conduct copy elision while bypassing the checks on the accessibility of the constructor being omitted?

CPPL
  • 726
  • 1
  • 10
  • Refer to [Is a prvalue the same as a temporary in C++17](https://stackoverflow.com/questions/71556308/is-a-prvalue-the-same-as-a-temporary-in-c17) – Jason Jul 05 '22 at 15:11

1 Answers1

2

X() is a prvalue.

In C++14, that meant that it was a temporary object. That means that X x = X() would try to copy initialize from the temporary object, and that would fail since the constructor is explicit. If it had succeeded, the compiler could optionally elide the copy as an optimization, but this is after checking that a copy could have happened in the first place.

In C++17, prvalues are not objects. They are instead expressions that can be used to initialize objects. In this case, the prvalue X() initializes x (by calling the default constructor). There is no copy or move to be elided in the first place, so the copy constructor isn't looked at. This is not an optimization, but just how prvalues are used.

"Mandatory copy elision"/"Guaranteed copy elision" is a bit of a misnomer: There are no copies elided, since there were no copies in the first place.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • I found that informative, thank you. I can't use C++17 ATM, unfortunately, but still... – Paul Sanders Jul 05 '22 at 15:05
  • Thank you for your answer :) I'm new to prvalue. So, can I say, in places where C++14 creates a temporary object, C++17 instead creates "a piece of information" (being the prvalue) first. If it is used to initialize another prvalue (of its type. E.g. `return X();`), such piece of information is just passed down to that prvalue; if it is used to initialize an object (of its type. E.g. `X x = X();`), the object is initialized according to such piece of information; only in other cases, such piece of information materializes into a temporary as in C++14. May I ask if my understanding is correct? – CPPL Jul 05 '22 at 15:57