bers
bers

Reputation: 5773

How to make (only) the main path virtual in the diamond problem

This code does not compile:

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : public BaseV1 {
public:
    DataV1() : BaseV1(1) {}
};


class BaseV2 : public BaseV1 {};

class DataV2 : public DataV1, public BaseV2 {};


int main() {
    DataV2().foo(); // request for member ‘foo’ is ambiguous
    return 0;
}

So I read about the diamond problem, and I came up with what I thought is this solution, which makes one complete path (DataV2 : BaseV2 : BaseV1) virtual. However, that does not change anything;

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : public BaseV1 {
public:
    DataV1() : BaseV1(1) {}
};


class BaseV2 : virtual public BaseV1 {};

class DataV2 : public DataV1, virtual public BaseV2 {};


int main() {
    DataV2().foo(); // request for member ‘foo’ is ambiguous
    return 0;
}

I made the example compile by making the other path (DataV2 : DataV1 : BaseV1) virtual, which is not really what I want, because I do not like touching the existing code base (V1) too much:

#include <iostream>

class BaseV1 {
public:
    BaseV1(int i = 0) {
        std::cout << i;
    }
    void foo() {}
};

class DataV1 : virtual public BaseV1 { // I do not want to make this virtual, I think.
public:
    DataV1() : BaseV1(1) {}
};


class BaseV2 : virtual public BaseV1 {};

class DataV2 : virtual public DataV1, public BaseV2 {
public:
    DataV2() : DataV1() {}
};


int main() {
    DataV1().foo(); // this prints 1, as expected
    DataV2().foo(); // this prints 0, not 1 - so DataV1's constructor is skipped
    return 0;
}

And even though the code does compile, it is failing at runtime as DataV1's constructor seems to be skipped.

So, my question is: how can I avoid the diamond problem here, ideally without touching the V1 inheritance path?

Upvotes: 0

Views: 72

Answers (1)

Mooing Duck
Mooing Duck

Reputation: 66922

virtual doesn't magically solve all problems. virtual inheritance of BaseV1 makes it so that the most-derived class directly inherits from BaseV1. But that means that DataV2 still has two BaseV1 classes - one it inherits from directly, and one inside DataV1.

Possible solutions:

  • Make DataV1 also have virtual inheritance (obviously)
  • Have DataV2 to simply not inherit from DataV1.
  • Keep everything as-is and give DataV2 its own void foo() method that delegates to a specific inner implementation (ex: void foo() {DataV1::foo();})

Upvotes: 2

Related Questions