1

I'm creating a unique_ptr to a struct (which works fine):

std::unique_ptr<w9::Product> product (new w9::Product(desc[i].desc, price[j].price));

After which, I am then appending that unique_ptr onto a vector member function.

object += product;

Which overloads the += as follows:

void operator+=(std::unique_ptr<Product> const &p) {
    object.push_back(&p);
}

The problem is this last bit. What is the proper way to push_back a unique ptr onto a vector?

EDIT:

Object is template class object that contains a vector as a data member. std::vector list

template <typename T>
class List {

    std::vector<T> list;
}

T in this case would be a 'unqiue_ptr'.

Actual code:

w9::List<w9::Product> merge(const w9::List<w9::Description> &desc, const w9::List<w9::Price>& price) {

w9::List<w9::Product> priceList;

for(int i = 0; i < desc.size(); i++){
    for(int j = 0; j < price.size(); j++){
        if(price[j].code == desc[i].code){
            std::unique_ptr<w9::Product> product (new w9::Product(desc[i].desc, price[j].price));
            product->validate();
            priceList += std::move(product);
        }
    }
}
return priceList;
}


void operator+=(std::unique_ptr<w9::Product> &&p) {
    list.push_back(std::move(p));
}

Error:

    ./List.h:41:12: error: no matching member function for call to 'push_back'
                list.push_back(std::move(p));
                ~~~~~^~~~~~~~~
w10.cpp:18:15: note: in instantiation of member function 'w9::List<w9::Product>::operator+=' requested here
                                priceList += std::move(product);
                                          ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:700:36: note: candidate function not viable: no
      known conversion from 'typename remove_reference<unique_ptr<Product, default_delete<Product> > &>::type' (aka 'std::__1::unique_ptr<w9::Product,
      std::__1::default_delete<w9::Product> >') to 'const value_type' (aka 'const w9::Product') for 1st argument
    _LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);
Flimzy
  • 60,850
  • 13
  • 104
  • 147
Kris
  • 7,608
  • 5
  • 23
  • 40

1 Answers1

4

std::unique_ptr is a class that assumes unique ownership over a resource. As such it doesn't support copy-semantics. Its copy-constructor and copy-assignment operators are deleted. In order to share a unique object, you need to transfer ownership in order to maintain the "uniqueness" of that object. This is done using move-semantics, which are implemented in the move-constructor and move-assignment operators of the std::unique_ptr class.

Your operator+=() method doesn't exactly violate the concept of unique ownership, but it tries to circumvent the restrictions a unique object implements. This doesn't make make much sense and isn't intuitive. Either you want to exchange ownership or you want to copy. If you want to copy the object, then maybe std::unique_ptr isn't the object you should be using. Ask yourself this: does product want to own another object or get a copy of one?

If your intention really is to share a unique object, you need to incorporate move-semantics in your function. You can bind an rvalue-reference to the argument and pass it on as an rvalue to the push_back() method:

void operator+=(std::unique_ptr<Product>&& p) {
    object.push_back(std::move(p));
}

Note that is the correct way to share a unique object. Once push_back() moves from your std::unique_ptr, the original argument will no longer have ownership over the Product resource. If this is not what you want, then std::shared_ptr can be used to copy the resource instead.

0x499602D2
  • 87,005
  • 36
  • 149
  • 233
  • I deleted my answer since this one is much better. – Veritas Dec 04 '14 at 21:50
  • Thank you so much. This makes sense. However passing the (priceList += product) into the overloaded operator throws: no known conversion from 'std::unique_ptr' to 'std::unique_ptr &&' Do I have to edit the operator call? – Kris Dec 04 '14 at 21:55
  • @SomeDeveloper You're passing an lvalue when it should be an rvalue-reference. Use `std::move()`: `x += std::move(ptr)`. – 0x499602D2 Dec 04 '14 at 21:56
  • That throws quite an ugly error. Let me edit my question with the actual code. – Kris Dec 04 '14 at 21:58
  • @SomeDeveloper What is that ugly error? – 0x499602D2 Dec 04 '14 at 22:01
  • I threw it into the main question. As a novice in C++, it can be quite daunting. – Kris Dec 04 '14 at 22:02
  • @SomeDeveloper It's basically saying that you have a `List` of `w9::Product` but you're passing in a `std::unique_ptr`. Now that you've shown me the code I can see that there's no need for a `std::unique_ptr` here anyway. You can just change `product` to `w9::Product product(desc[i].desc, price[j].price)` and change your `operator +=` function to take that argument as well. – 0x499602D2 Dec 04 '14 at 22:06
  • I know what you're saying. Its for an assignment actually, and the last part is to convert the raw pointer over to a unique_ptr. I had that exact code prior, so now i'm just trying to figure out this unique_ptr stuff – Kris Dec 04 '14 at 22:08
  • @SomeDeveloper Alrighty, just change `List priceList` to `List> priceList`. – 0x499602D2 Dec 04 '14 at 22:09
  • Wouldn't I then have to change the return value from priceList to a unique_ptr? – Kris Dec 04 '14 at 22:12
  • @SomeDeveloper Yes, that's right. – 0x499602D2 Dec 04 '14 at 22:14
  • Thanks a ton! I really appreciate it. – Kris Dec 04 '14 at 22:17