3

I have a base class:

class Base {
public:
    Base(??? new_p) : p(new_p) {} 
    std::unique_ptr<MyType> p;
}

And a derived class:

class Derived : public Base {
    Derived(??? new_p) : Base(new_p) {}
}

What type do I replace the question marks with if I want to construct Derived? Other changed are also fine. I want to make sure Derived can be constructed without copying the MyType that is pointed to by p.

Drew
  • 12,578
  • 11
  • 58
  • 98
  • How about `MyType *`? – Mohit Jain Apr 17 '15 at 06:19
  • I tried that and had errors with the `MyType` getting `free`d before I expected it. Maybe that was a different problem, – Drew Apr 17 '15 at 06:21
  • Ownership of MyType* will guide decision for what you should use for ???. This will range from using std::unique_ptr to just MyType*. In both cases you may also like to check if MyType needs to be deep copied or not. – nature1729 Apr 17 '15 at 06:30

3 Answers3

1

Depends what you want to support - either or both of the constructors below make sense, from MyType* or a std::unique_ptr<MyType>&&, which requires a movable unique_ptr be supplied by the caller. Simply using std::unique_ptr<MyType> works too, because std::unique_ptr has a constructor from other movable instances... just a matter of taste whether you want to emphasise the necessarily-transient nature of the caller's incoming unique_ptr in your own code.

class Base
{
  public:
    Base(MyType* new_p) : p(new_p) { } 
    Base(std::unique_ptr<MyType>&& new_p) : p(std::move(new_p)) { } 

    std::unique_ptr<MyType> p;
};

class Derived : public Base
{
  public:
    Derived(MyType* new_p) : Base(new_p) { }
    Derived(std::unique_ptr<MyType>&& new_p) : Base(std::move(new_p)) { }
};

See it running here

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • When I try `Base(std::unique_ptr&& new_p) : p(std::move(new_p)) { }` I get an error about `Call to implicitly-deleted copy constructor of 'std::unique_ptr`, maybe due to MyType being abstract? I didn't think that would be relevant, but maybe it is. – Drew Apr 17 '15 at 06:35
  • @Drew Is the error happening when your caller's passing a named variable without `std::move()`? It should work for temporaries (technically, expiring values) and when `std::move()` explicitly grants it permission to steal the caller's variable's state. – Tony Delroy Apr 17 '15 at 06:42
  • 1
    Yeah, I didn't realize I had to `move` in the caller as well. Thanks! – Drew Apr 17 '15 at 06:55
1

I'd replace ??? with std::unique_ptr<MyType> and then std::move it in the mem-initializer.

class Base {
public:
    Base(std::unique_ptr<MyType> new_p) : p(std::move(new_p)) {} 
    std::unique_ptr<MyType> p;
};

class Derived : public Base {
    Derived(std::unique_ptr<MyType> new_p) : Base(std::move(new_p)) {}
};

You could also use std::unique_ptr<MyType>&& instead of std::unique_ptr<MyType> and avoid the std::moves but I prefer the by-value approach for the reasons listed in this answer.

I would recommend against taking a MyType * argument. The problem with that solution is that it doesn't convey to the user your intent of taking ownership of the pointer passed to the constructor.

Community
  • 1
  • 1
Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

This worked for me. Edit to note I'm using string as the type only to make it easier to read, you must replace it with your type.

#include <memory>
#include <string>
#include <iostream>
#include <utility>

class Base {
public:
    Base(std::unique_ptr<std::string> new_p) 
      : p(std::move(new_p)) {} 
    std::unique_ptr<std::string> p;
};

class Derived : public Base {
public:
    Derived(std::unique_ptr<std::string> new_p) 
      : Base(std::move(new_p)) {}
};

int main(){
    std::unique_ptr<std::string> text(new std::string("Hello world"));

    Derived a(std::move(text));

    std::cout << *(a.p);
}
Jose Palma
  • 756
  • 6
  • 13
  • you should use std::move instead of std::forward. You have rvalue references not forwarding one – grisha Apr 17 '15 at 06:29
  • Modified already, but consider that forward by definition keeps the "value type", lvalue or rvalue. "When used according to the following recipe in a function template, forwards the argument to another function with the value category it had when passed to the calling function" – Jose Palma Apr 17 '15 at 06:32