-1

I am attempting to update some code that hands off a new object. The goal is to manage it with a smart pointer. Boiled down, it looks much like this:

class X
{
    shared_ptr<Y> yptr;

    X() : yptr(Y::create(new Z())){}
};

class Y
{
    Z* zptr;
    static shared_ptr<Y> create(Z* zp)
    {
        if(!zp) return nullptr;
        else return shared_ptr<Y>(new Y(zp));
    }

    Y(Z* zp) : zptr(zp){}
}

So far this seems to work:

class X
{
    shared_ptr<Y> yptr;

    X() : yptr(Y::create(  std::move(  std::make_unique<Z>(Z())  )  )){}
};

class Y
{
    unique_ptr<Z> zptr;
    static shared_ptr<Y> create(unique_ptr<Z> zp)
    {
        if(!zp) return nullptr;
        else return shared_ptr<Y>(new Y(std::move(zp)));
    }

    Y(unique_ptr<Z> zp) : zptr(std::move(zp)){}
}

My question is, is the first std::move() (around make_unique) necessary? Visual Studio doesn't seem to mind either way. I'd rather have an accurate understanding before I start making similar changes elsewhere where performance is more critical.

M. Parker
  • 23
  • 1
  • 7
  • 2
    What's wrong with using `std::make_shared()` if you actually want a `std::shared_ptr`? – πάντα ῥεῖ May 07 '19 at 17:55
  • All that `std::move` does is cast the object to a prvalue. If the object already is a prvalue (for example, it doesn't have a name... it's a temporary), then `std::move` is superfluous. – Eljay May 07 '19 at 18:00
  • @πάντα-ῥεῖ The code involved is a factory method that assigns its members on creation. The object returned by it is intended to be the sole owner of the item managed by the unique_ptr. A shared_ptr would work in this case but it betrays the intent. – M. Parker May 14 '19 at 13:29
  • @françois-andrieux And it is, I'll adjust the above. :) – M. Parker May 14 '19 at 13:31

3 Answers3

0

Short answer: Yes

The function call std::make_unique<Z>() returns an rvalue, so there is no need to wrap it in std::move() if you are passing it directly into another function.

Kyle Willmon
  • 709
  • 3
  • 13
0

My question is, is the first std::move() (around make_unique) necessary?

if you mean this line:

X() : yptr(Y::create(  std::move(  std::make_unique<Z>(Z())  )  )){}

then no, it is completely unnecessary as std::make_unique<Z> returns temporary which is already prvalue. You can do something like this:

auto ptr = std::move( std::move( std::move( std::make_unique<Z>() ) ) );

instead of

auto ptr = std::make_unique<Z>();

but all of that moves are unnecessary and the fact it is compiled clearly shows that.

Slava
  • 43,454
  • 1
  • 47
  • 90
0

Yes! The move around make_unique is unnecessary.

std::move exists to convert an l-value reference & (which refers to an existing object) into an r-value reference && (which represents a temporary value, or something that can be moved from).

If you assign a unique_ptr to a named variable, then you indeed need to std::move from it if you want to transfer its ownership (usually; see below). You cannot directly bind an r-value reference to a variable.

The value returned by an expression or function call, such as the unique_ptr returned by make_shared, is a temporary, and so it naturally can bind to an r-value expression and be moved-from implicitly.

Note that there are some special cases where you do and don't need to use std::move:

  • If you have a named variable whose type is an r-value reference, you do need to std::move it to avoid passing it by copy:

    MyClass(std::string&& s) : m_mystring(s) {} copy-constructs m_mystring

    MyClass(std::string&& s) : m_mystring(std::move(s)) {} move-constructs m_mystring

    In general, variables with names do no implicitly get moved from.

  • If you are returning an local variable by value from a function, that object may get moved from automatically, in the so-called Named Return Value Optimization. This gives a nice speed boost to returning by value. You don't need to think much about this point in practice, just be aware that return std::move(whatever); is almost always wrong.

alter_igel
  • 6,899
  • 3
  • 21
  • 40
  • Excellent explanation. I have a followup, since you brought up a similar situation. If the function that the make_unique is being passed into has to pass it on to a constructor before it is actually used, is the std::move() now necessary, or is it only needed inside the constructor itself (the final destination)? – M. Parker May 13 '19 at 18:36
  • @M.Parker From Scott Meyers' _Essential Modern C++_, "a parameter is always an lvalue, even if its type is an rvalue reference. That is, given `void f(Widget&& w);`, the parameter `w` is an lvalue, even though its type is rvalue-reference-to-`Widget`." – alter_igel May 13 '19 at 19:21
  • This might seem like a minor point, but the question was "is the first std::move() *necessary*", (not "is the first std::move() *un*necessary). So your answer is in fact "No!", not "Yes!". I got derailed for several minutes trying to make sense of your answer, because I saw your "Yes!" but missed your "un", and thought you were saying the opposite of what you are actually saying. – Don Hatch Apr 26 '20 at 12:14