3

I have the following C++11 construct:

#include <vector>
#include <memory>

class X {
public:
        void f(void) { }
};

class Y : public X {
public:
        void g(void) { }
};

class A {
private:
        std::vector <std::unique_ptr <X> > xs;
public:
        void addX(std::unique_ptr <X> &ref) {
                xs.push_back(std::move(ref));
        }
};

int main() {
        A a;

        std::unique_ptr <Y> y(new Y());
        y->f();
        y->g();

        std::unique_ptr <X> x(y.release());
        a.addX(x);

        return 0;
}

In the main function I am trying to build an object of type Y and then add it to the vector of X objects of a. However, I can not directly say a.addX(y) as std::unique_ptr <Y> cannot be cast to std::unique_ptr <X>&. This is why I came up with the workaround where I initialize another unique pointer, x, with the released internal pointer of y.

Although this works, it doesn't seem like the best solution. Is there a better way of passing an object of type std::unique_ptr<Y> as an argument of type std::unique_ptr<X>&?

Thanks, - Stan

stanm
  • 3,201
  • 1
  • 27
  • 36

2 Answers2

5

std::unique_ptr provide already the right overload but you need std::move to use them as unique_ptr is not copyable :

std::unique_ptr<X> x;
std::unique_ptr<Y> y { new Y };
x = std::move(y);

For the very specific question, there is no cast possible, if you need to received unique_ptr of sub classes by reference, then use a template function.

template < typename T, typename = typename std::enable_if< std::is_base_of<X,T>::value>::type >
void foo( std::unique_ptr<T> & ) {
}

And last, as the idea is to get ownership of the pointer, if you pass the unique_ptr by rvalue reference, this works as you wants.

void bar( std::unique_ptr<X> && ) {
}
// then
bar( std::move(y) );
galop1n
  • 8,573
  • 22
  • 36
  • So, actually, the OP can just write `a.addX(std::move(y));`, can't he? – rodrigo Feb 05 '14 at 14:51
  • Okay, this looks better, but still involves the creation of the object x, which seems wasteful. Any ideas why `a.addX(std::move(y))` doesn't work? – stanm Feb 05 '14 at 14:53
  • Note: `a.addX(std::move(y))` results in an error: `unique.cpp:31:32: error: no matching function for call to ‘A::addX(std::remove_reference&>::type)’` – stanm Feb 05 '14 at 14:55
  • @stanm exactly how expensive do you think creating a `unique_ptr` is? – Yakk - Adam Nevraumont Feb 05 '14 at 16:07
  • @Yakk about 25 keystrokes :) If you're asking because of the passing by reference, it was to avoid copying ... but of course that would not be allowed for the unique pointer, so I shouldn't have worried about it. This [post](http://www.drdobbs.com/cpp/c11-uniqueptr/240002708) confused me though. I just met unique pointers yesterday, so I am still learning. – stanm Feb 05 '14 at 16:29
  • 2
    @stanm do not take `unique_ptr` by `&`. You can take it by value, `const&` (although in that case, just take a raw pointer usually), or by `&&` (I would generally say to just take it by value instead of `&&`, as `unique_ptr` are dirt cheap to copy, and a copied `unique_ptr` means your data is always taken, while a `&&` means it is only maybe taken, which leads to confusion at the call site.). – Yakk - Adam Nevraumont Feb 05 '14 at 16:36
2

You may use a rvalue reference and delegate it with std::move:

#include <vector>
#include <memory>

class X {
public:
        void f(void) { }
};

class Y : public X {
public:
        void g(void) { }
};

class A {
private:
        std::vector <std::unique_ptr<X> > xs;
public:
        void addX(std::unique_ptr <X>&& ref) {
                xs.push_back(std::move(ref));
        }
};

int main() {
        std::unique_ptr <Y> y(new Y());
        y->f();
        y->g();

        A a;
        a.addX(std::move(y));

        // error: no matching function for call to ‘A::addX(std::unique_ptr<Y>&)’
        // a.addX(y);

        return 0;
}

Your function void addX(std::unique_ptr <X> &ref) stealing the content internally might be a questionable design.

  • Sorry, apparently I wasn't clear enough, but I want to keep it in the class A; the code I gave is a simplification of a more complex program. My question is the last paragraph of my original post. – stanm Feb 05 '14 at 15:00
  • 2
    The above code does not use the technique of 'universal references' despite claiming to. It uses rvalue references, which is a related (both syntactically and conceptually) technique. – Yakk - Adam Nevraumont Feb 05 '14 at 16:08
  • 1
    forward and universal references works only with template functions, here you are using a mere rvalue reference and using std::forward is more verbose than std::move. – galop1n Feb 05 '14 at 16:17
  • @galop1n have a look at http://stackoverflow.com/questions/9671749/whats-the-difference-between-stdmove-and-stdforward –  Feb 05 '14 at 16:28
  • @DieterLücking read again the answer on the question you give in your comment. "std::forward has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it". addX is not template, then std::forward have no means here ! – galop1n Feb 05 '14 at 16:33
  • @DieterLücking your answer is fine and is the right solution, just replace forward with move. – galop1n Feb 05 '14 at 16:38
  • @galop1n Sorry, someone rejected your edit while I was reading the comments before approving. Try it again. And add "please read comments" in the description. – manuell Feb 05 '14 at 16:44