Search code examples
c++polymorphismstdsubclassunique-ptr

Abstract Class in std::unique_ptr as a Return from Function not Working


class Base{
public:
    virtual std::string typeName() = 0;
    virtual ~Base() = 0;
};
Base::~Base(){}

class D1 : public Base{
public:
    std::string typeName();
    std::vector<std::unique_ptr<Base>> children;
    ~D1(){};
};
class D2 : public Base{
public:
    std::string typeName();
    std::unique_ptr<Base> child;
    ~D2(){};
};
std::string D1::typeName(){
    return "class D1";
}
std::string D2::typeName(){
    return "class D2";   
}

class Program{
public:
   std::unique_ptr<Base> D1Generic();
   std::unique_ptr<Base> D2Generic();
};
std::unique_ptr<Base> Program::D1Generic(){
    return std::make_unique<Base>(D1{});
}
std::unique_ptr<Base> Program::D2Generic(){
    return std::make_unique<Base>(D2{});
}

This code is just a simplified version that replicates the problem I'm facing. My intention with this design is to create a visitor pattern on a tree, and turn that into another tree.

I've narrowed down the immediate problem to the D1Generic and D2Generic functions. My intention here was to create an expandable interface that allows multiple subclasses of Base to fit as a unique pointer variable polymorphically.

I got these errors:

/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/unique_ptr.h:821:34: error: 
      allocating an object of abstract class type 'Base'
    { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
                                 ^
main.cpp:38:17: note: in instantiation of function template specialization
      'std::make_unique<Base, D1>' requested here
    return std::make_unique<Base>(D1{});
                ^
main.cpp:8:25: note: unimplemented pure virtual method 'typeName' in 'Base'
    virtual std::string typeName() = 0;
                        ^
main.cpp:9:13: note: unimplemented pure virtual method '~Base' in 'Base'
    virtual ~Base() = 0;
            ^
In file included from main.cpp:4:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/memory:80:
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/bits/unique_ptr.h:821:34: error: 
      allocating an object of abstract class type 'Base'
    { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
                                 ^
main.cpp:41:17: note: in instantiation of function template specialization
      'std::make_unique<Base, D2>' requested here
    return std::make_unique<Base>(D2{});
                ^
2 errors generated.

Solution

  • The underlying issue is explored in Abstract class and unique pointer, but solutions details are different.

    A call to std::make_unique<T> dynamically allocates an object of type T. The parameters do not affect what is created, only how it is initialized. When you provide an object of type D1 to initialize a dynamically-allocated object of type Base, a reference to the D1 object is cast to a reference to its Base sub-object, and the new object is copy-constructed from that.

    What you (presumably) want to do is dynamically allocate an object of type D1, then cast its address to a pointer-to-Base. That is, cast after construction.

    return std::make_unique<D1>();
    

    This dynamically allocates an object of type D1, creating a unique_ptr to the new object. This smart pointer is returned via move semantics. That means the returned object (declared as unique_ptr<Base>) is move-constructed from the unique_ptr<D1> returned by make_unique. Fortunately, this is allowed!