I have the following minimal reproducible example where it seems that my unique-pointer-containing object B
is not allowed to contain a unique pointer to another instance of B
:
#include <iostream>
#include <memory>
struct C {
virtual void say_name() = 0;
};
struct A : public C {
int value;
A(int value) : value(value) {}
void say_name() override { std::cout << "A" << value << std::endl; }
};
struct B : public C {
std::unique_ptr<C> ptr;
B(std::unique_ptr<C> ptr) : ptr(std::move(ptr)) {}
void say_name() override { std::cout << "B" << std::endl; }
};
int main() {
A a(5);
B b(std::make_unique<A>(10));
// Here I create another B - causes error
B b2(std::make_unique<B>(b));
}
I expected the following to happen:
I expected b
to own a
, and b2
to own b
which owns a
.
However, I get an error message when I try and create b2
that says I have a call to an implicitly deleted copy-constructor of B
.
I don't understand the error or how to fix it and want to understand how I should restructure my program so that I can nest objects in this way.
Here is the full error message:
In template: call to implicitly-deleted copy constructor of 'B'clang(ovl_deleted_special_init)
unique_ptr.h(767, 30): Error occurred here
main.cpp(33, 13): In instantiation of function template specialization 'std::make_unique<B, B &, 0>' requested here
main.cpp(24, 22): Copy constructor of 'B' is implicitly deleted because field 'ptr' has a deleted copy constructor
unique_ptr.h(221, 55): Copy constructor is implicitly deleted because 'unique_ptr<C>' has a user-declared move constructor
Problem is this expression:
std::make_unique<B>(b)
It attempts to create a copy of b
.
Since B
contains uniqe_ptr
B
is not copyable it is only moveable. So one way to fix it is to move b
.
B b2(std::make_unique<B>(std::move(b)));
https://godbolt.org/z/qGGe1qv8Y
Other way to approach this it to provide own version of copy constructor. Problem is to archive this you need a way to duplicate ptr
member variable. This can be done by adding functionality to C
called clone
:
#include <iostream>
#include <memory>
struct C {
virtual void say_name() = 0;
virtual std::unique_ptr<C> clone() const = 0;
virtual ~C() = default;
};
struct A : public C {
int value;
A(int value)
: value(value)
{
}
void say_name() override { std::cout << "A" << value << std::endl; }
std::unique_ptr<C> clone() const override {
return std::make_unique<A>(value);
}
};
struct B : public C {
std::unique_ptr<C> ptr;
B(std::unique_ptr<C> ptr)
: ptr(std::move(ptr))
{
}
B(const B& other) : ptr{other.ptr->clone()} {
}
void say_name() override { std::cout << "B" << std::endl; }
std::unique_ptr<C> clone() const override {
return std::make_unique<B>(*this);
}
};
int main()
{
A a(5);
B b(std::make_unique<A>(10));
B b2(std::make_unique<B>(b));
}
https://godbolt.org/z/eM663Yf1z
Depending on what you wish to achieve you have to select proper solution.