Reputation: 5773
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
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:
DataV1
also have virtual inheritance (obviously)DataV2
to simply not inherit from DataV1
.DataV2
its own void foo()
method that delegates to a specific inner implementation (ex: void foo() {DataV1::foo();}
)Upvotes: 2