Search code examples
c++referenceunique-ptrmove-semanticsforwarding-reference

passing reference argument to function taking universal reference of unique_ptr


I have two functions whose signatures I can't change. The first one takes a reference to an object, while the second takes a universal reference to a unique pointer of the same object. I'm not sure how to pass the first argument to the second.

I've tried by passing a new unique ptr with the address of the reference, but because the reference is abstract, the compiler complains about the unimplemented methods.

Here is an example:

// Example program
#include <iostream>
#include <memory>


class MyAbstractClass {
public:
  virtual void doSomething() = 0;    
};

class MyConcreteClass : public MyAbstractClass {
public:
  void doSomething() override {
    std::cout << "Hello I do something" << std::endl;
  }
};

class MyUserClass {
public:
  MyUserClass(MyAbstractClass& mac){
    // the line below doesn't work !!
    doSomethingElse(std::make_unique<MyAbstractClass>(&mac));
  }
  void doSomethingElse(std::unique_ptr<MyAbstractClass>&& mac){
    mac->doSomething();
  }  
};


int main() {
  MyConcreteClass mcc;
  MyUserClass muc(mcc);
}

Solution

  • Your compiler complains because make_unique calls new on the type you are trying to instantiate, effectively copying the existing object. Of course, it can't do that, as the class is abstract.

    Unless you have a way to guarantee that the reference passed to MyUserClass is to a dynamic ("heap") variable (and its pointer is not owned, etc.), you cannot just capture its pointer in unique_ptr and then release it after doSomethingElse. Even if this is guaranteed, for all you know, doSomethingElse could still try to delete the pointer itself (unless you know exactly what it does). As you pass it by rvalue reference, the object may not as well be valid after doSomethingElse returns.

    That means you have to make a copy of the object. You can't make a plain copy though. You need to do a copy of the entire, non-abstract, underlying object.

    If you are allowed to change signature of the classes, adding a virtual clone method would to the trick.

    If not, a not-absolutely-terrible workaround could be (you need to know beforehand all the types that inherit MyAbstractClass though) switch(typeid(mac)) and dynamic_cast.