There is a concept in C++ called elision.
Elision takes two seemingly distinct objects and merges their identity and lifetime.
Prior to c++17 elision could occur:
- When you have a non-parameter variable - Foo f;in a function that returned- Fooand the return statement was a simple- return f;.
 
- When you have an anonymous object being used to construct pretty much any other object. 
In c++17 all (almost?) cases of #2 are eliminated by the new prvalue rules; elision no longer occurs, because what used to create a temporary object no longer does so.  Instead, the construction of the "temporary" is directly bound to the permanent object location.
Now, elision isn't always possible given the ABI that a compiler compiles to.  Two common cases where it is possible are known as Return Value Optimization and Named Return Value Optimization.
RVO is the case like this:
Foo func() {
  return Foo(7);
}
Foo foo = func();
where we have a return value Foo(7) which is elided into the value returned, which is then elided into the external variable foo.  What appears to be 3 objects (the return value of foo(), the value on the return line, and Foo foo) is actually 1 at runtime.
Prior to c++17 the copy/move constructors must exist here, and the elision is optional; in c++17 due to the new prvalue rules no copy/move constructr need exist, and there is no option for the compiler, there must be 1 value here.
The other famous case is named return value optimization, NRVO.  This is the (1) elision case above.
Foo func() {
  Foo local;
  return local;
}
Foo foo = func();
again, elision can merge the lifetime and identity of of Foo local, the return value from func and Foo foo outside of func.
Even c++17, the second merge (between func's return value and Foo foo) is non-optional (and technically the prvalue returned from func is never an object, just an expression, which is then bound to construct Foo foo), but the first remains optional, and requires a move or copy constructor to exist.
Elision is a rule that can occur even if eliminating those copies, destructions and constructions would have observable side effects; it is not an "as-if" optimization.  Instead, it is subtle change away from what a naive person might think C++ code means.  Calling it an "optimization" is more than a bit of a misnomer.
The fact it is optional, and that subtle things can break it, is an issue with it.
Foo func(bool b) {
  Foo long_lived;
  long_lived.futz();
  if (b)
  {
    Foo short_lived;
    return short_lived;
  }
  return long_lived;
}
in the above case, while it is legal for a compiler to elide both Foo long_lived and Foo short_lived, implementation issues make it basically impossible, as both objects cannot both have their lifetimes merged with the return value of func; eliding short_lived and long_lived together is not legal, and their lifetimes overlap.
You can still do it under as-if, but only if you can examine and understand all side effects of destructors, constructors and .futz().