5

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;
}
sorush-r
  • 10,490
  • 17
  • 89
  • 173

2 Answers2

7

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)) {}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
-1

What's wrong with asking the compiler to provide the implementation?

d3(d3 &&) = default; // I think this the best approach as it is less error pron .

You could write it out if you really wanted to:

d3(d3 &&other): base(std::move(other)), d1(std::move(other)), d2(std::move(other)) {

}