4

I'm able to pass a std::unique_ptr rvalue as a parameter to a function like below because of copy elision. Is the copy guaranteed to be elided by the C++11 standard or can this fail to compile on some implementations?

void take_unique_ptr_by_value(std::unique_ptr<int> sp) {
  cout << "Value is " << *sp.get() << std::endl;
}
// I am able to call the function above like this:
take_unique_ptr_by_value(std::make_unique<int>(3));
Mike Sweeney
  • 1,896
  • 2
  • 18
  • 20

2 Answers2

6

because of copy elision

No, it is because of move constructor.

In addition, copy elision is just a optimization and still requires the code to be valid. So

struct S
{
    S() = default;
    S(const S&) = delete;
    S(S&&) = delete;
};

S s = S(); // Won't compile prior C++17

C++17 introduces "guarantied copy elision" (in some contexts) which removes this constraint.

More details on copy_elision's doc

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    To be fair, though the c++ syntax says there is a move here, many compilers will optimise the construction directly into the function's argument space, so there may be elision as well. Perhaps the O/P saw this optimisation under the bugger? I don't think C++17 would guarantee elision here. – Gem Taylor May 15 '19 at 09:51
  • @GemTaylor: in OP code, guaranty elision of C++17 applies. – Jarod42 May 15 '19 at 10:12
  • yes, you are right. value parameters are mentioned. I might take advantage of that sometime. – Gem Taylor May 15 '19 at 10:16
  • @GemTaylor You're right that in my case the construction was optimized directly into the function's argument space. I also thought even if there was no copy construction and a move construction that would also be considered "copy elision". – Mike Sweeney May 15 '19 at 15:10
  • @Jarod42 I was aware that some of this changed in C++17 which is why I said C++11. Are you saying in C++11 that the move constructor is guaranteed to be called here in C++11? – Mike Sweeney May 15 '19 at 15:11
  • @MikeSweeney: Prior to c++17, copy elision might happen in your sample, **but** copy or move contructor should be available (code should be valid even if optimization doesn't occurs (as it is optional)). – Jarod42 May 15 '19 at 15:19
  • @Jarod42 Can you tell me why in my example the move constructor would be applied as opposed to the copy constructor since it's a by value parameter? I understand your example but that's different than mine. I'm trying to reconcile with this: https://abseil.io/tips/117 – Mike Sweeney May 16 '19 at 05:38
  • 1
    Temporary created by `std::make_unique(3)` has no name, we have a rvalue. So move constructor is better match than (deleted) copy constructor. – Jarod42 May 16 '19 at 08:08
0

To piece together some of the answers more explicitly as @Jarod42 mentioned even if copy elision were to take place the copy/move constructor being elided still has to exist. Secondly, what I was forgetting is that the parameter I'm passing in is an rvalue so it will invoke the move constructor if it needs to be "copied". So copy elision or not the example code is correct because std::unique_ptr has a move constructor and I am passing in an rvalue. This other answer is pretty comprehensive.

Mike Sweeney
  • 1,896
  • 2
  • 18
  • 20