Consider following diamond-like multiple inheritance:
class base;
class d1 : virtual public base;
class d2 : virtual public base
class d3 : public d1, public d2;
base
is a move-only class (having a large move-only buffer). So are d1
, d2
and d3
. Move constructor of d1
and d2
call move constructor of the base
.
Then what should do move constructor of d3
? Calling both move-ctors of d1 and d2 results in crashs (since move constructor of base
is called twice.
Here I have a minimum compilable instance of the problem:
#include <iostream>
struct moveonly {
moveonly(): data(nullptr) {}
moveonly(const moveonly &) = delete;
moveonly(moveonly &&other) {
this->data = other.data;
other.data = nullptr;
}
~moveonly() {
if(data)
delete[] data;
}
char *data;
};
class base {
public:
base() = default;
base(const base &) = delete;
base(base &&other) : d(std::move(other.d)) { }
virtual ~base() = default;
int a;
int b;
moveonly d;
};
class d1 : virtual public base {
public:
d1() = default;
d1(const base &) = delete;
d1(d1 &&other) : base(std::move(other)) { }
int x;
int y;
};
class d2 : virtual public base {
public:
d2() = default;
d2(const base &) = delete;
d2(d2 &&other) : base(std::move(other)) { }
int r;
int s;
};
class d3 : public d1, public d2 {
public:
d3() = default;
d3(const base &) = delete;
// What should I do here?
d3(d3 &&other) : d1(std::move(other)), d2(std::move(other)) { }
int p;
int q;
};
int main()
{
d3 child;
child.d.data = new char[1024];
for(size_t i = 0; i < 1024; ++i)
child.d.data[i] = i * 2;
d3 other_child = std::move(child);
for(size_t i = 0; i < 1024; ++i) {
std::cerr << other_child.d.data[i] << ' ';
}
std::cerr << std::endl;
return 0;
}
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.
A move constructor allows the resources owned by an rvalue object to be moved into an lvalue without creating its copy. An rvalue is an expression that does not have any memory address, and an lvalue is an expression with a memory address.
The move constructor is not generated because you declared a copy constructor. Remove the private copy constructor and copy assignment. Adding a non-copyable member (like a unique_ptr ) already prevents generation of the copy special members, so there's no need to prevent them manually, anyway.
As with all virtual inheritance, the virtual bases are initialized by the most-derived object, so:
d3(d3 &&other)
: base(std::move(other)), // <== *you* initialize the base
d1(std::move(other)),
d2(std::move(other)) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With