2

I'm trying to learn about resource management in C++, and in my studies I've encountered an interesting optimization. Basically, when initializing an object on the stack with a copy constructor where the object is an rvalue Object (is it rvalue?), instead of calling the constructor and then calling the move constructor, the compiler simply calls the original Object's constructor.

Object c(Object(1)); // This is the same as Object c(1);
Object c(Object(Object(Object(Object(Object(1)))))); // This is also the same as Object c(1);
Expected flow:
1. Object(1) calls the constructor and creates a nameless Object that will be removed as soon as it's created.
2. c notices this is an rvalue, and calls the move constructor.
3. Destructor for Object(1) is called.
Actual flow:
1. c(1) is called.

This is smart, but.. How? What's the mechanism behind this trick? This works even if the constructor for Object accepts pointers and many arguments.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Alex Osheter
  • 589
  • 5
  • 22
  • Does it have something to do with this? https://stackoverflow.com/questions/35506708/move-constructor-vs-copy-elision-which-one-gets-called – Carlos Feb 02 '20 at 19:05
  • I don't think so. From what I can tell, that thread talks about functions that return objects by value and template/not template. If you're implying copy elision is what happens here, I'm not sure. No textbook shows copy elision like this. In classic examples, the object is already constructed. In this case, it's not. – Alex Osheter Feb 02 '20 at 19:14
  • https://stackoverflow.com/questions/38043319/how-does-guaranteed-copy-elision-work – M.M Feb 02 '20 at 19:15
  • You tag C++11 -- do you mean specifically C++11 and not C++14 or C++17? (The meaning of this code changed in C++17) – M.M Feb 02 '20 at 19:16
  • I mean specifically C++11. Reading the post you linked now :) – Alex Osheter Feb 02 '20 at 19:17

1 Answers1

5

Prior to C++17 , this behaviour falls under copy elision. Object(x) specifies creating a temporary object, but the compiler can, at its own discretion, omit creating and destroying all the temporary objects in some scenarios.

Since C++17 this changed, now Object(x) means that there may, sooner or later, be an Object created with initializer x . That object is called the result object and the identity of the result object is determined by the context this expression appears in, in your code c is the result object for all of the expressions of that form, and both lines are exactly identical to Object c(1); . This applies to all expressions of category prvalue .

The latter provides more certainty for coders that unnecessary copies won't be made.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • By that logic, does this code ```Object c(f())``` also do copy elision? ```f()``` creates and returns a temporary object, and the compiler can, at its own discretion, omit creating and destroying the object created inside the function? – Alex Osheter Feb 02 '20 at 19:29
  • 3
    @AlexOsheter yes, supposing `f()` returns an `Object` by value, then that is a copy elision context pre-C++17, and since C++17 the *result object* of the function is `c` . The `f()` may or may not create other objects in addition to its result object, depending on how `f()` is coded – M.M Feb 02 '20 at 19:31
  • I'm asking because in a presentation from our professor, this is an example for a move constructor. ```enter f() -> constructor called for temp object -> return by value -> move constructor called -> destructor for temp is called```. So, are both answers correct? There's no way to know in C++11? Completely compiler dependent? – Alex Osheter Feb 02 '20 at 19:39
  • 2
    @AlexOsheter yes it's up to the compiler (supposing `f()` returns `Object` by value). – M.M Feb 02 '20 at 19:40
  • 1
    and in case an elision doesn't happen - is my expected flow correct? I'm marking this answer as correct because it answers the question, but I'm just curious what would happen if the compiler decided elision shouldn't happen here. – Alex Osheter Feb 02 '20 at 19:42
  • 1
    @AlexOsheter yes that is correct (pre-C++17) . Although "constructor called for temp object" is a part of "return by value", it won't happen until the return statement is being executed – M.M Feb 02 '20 at 19:44