2

For example, I have a class

class A
{
public:
    // argument with default value is too long, any better way
    A(std::unique_ptr<int> data = std::unique_ptr<int>(new int(10))) 
        : mData(std::move(data))
    {}

    // Is r-value better? Is the following code okay?
    A(std::unique_ptr<int>&& data = std::unique_ptr<int>(new int(10)))
        : mData(data)
    {}

private:
    std::unique_ptr<int> mData;
};
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
user1899020
  • 13,167
  • 21
  • 79
  • 154
  • 3
    Well you could have a default constructor: `A(): mData(new int(10)) {}` – Borgleader Jul 27 '13 at 20:41
  • Do you want a shorter code? the second constructor will only accept r-values and data will be copied not explicitly moved to mData as you did in the first constructor – a.lasram Jul 27 '13 at 20:45

1 Answers1

4

At first glance, it seems that you can do this:

A (std::unique_ptr<int> data = {new int(10)}) 
    : mData(std::move(data))
{}

Note the use of curly braces (i.e. uniform initialization.) However, this does not work (does not compile,) because the unique_ptr constructor that accepts a pointer is declared as explicit, which precludes its use here.

One more thing you can do is this:

A (int * data = new int(10)) 
    : mData (data)
{}

That is, accepting a normal pointer and constructing your member from it. And you won't need to move from it either (the std::move is still recommended though.)

This one does compile and does work, but it has the fatal flaw that although you are taking over the ownership of the passed in pointer (you are going to delete it,) you are not showing this behavior in your interface. So, this new interface will be less informative than the previous one.

One more way is to write a shortcut function template, like so:

template <typename T>
std::unique_ptr<T> uptr (T * v)
{
    return std::unique_ptr<T>(v);
}

And then, your constructor becomes:

A(std::unique_ptr<int> data = uptr(new int(10))) 
    : mData(std::move(data))
{}

But in my opinion, this is not much of an improvement.

However, if this particular use case is your whole problem, why not just go ahead and define a default constructor and a single-argument constructor like this:

A () 
    : mData (new int (10))
{}

/*explicit*/ A (std::unique_ptr<int> data)
    : mData(std::move(data))
{}

This is just more sensible. (Note that putting explicit there is a good idea and good form, but not related to your problem so I've commented it out.)

Also, your second constructor does not actually move anything. The r-value reference type does not work as you think it does in this case. In short, since data has a name, it's no longer an r-value, although its type is still r-value reference to something.

yzt
  • 8,873
  • 1
  • 35
  • 44