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.
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!