Apart from the static_assert(...)
to help fix the problem with the virtual inheritance you can use the keyword override
in the derived classes as such:
class Base {
public:
virtual doSomething() = 0; // purely virtual
};
class DerivedA : public Base {
public:
virtual doSomething() override;
};
class DerivedB : public Base {
public:
virtual doSomething() override;
};
This way it should help to avoid any ambiguity.
Edit
I'm adding this section to see if it helps the OP:
template <class T>
class Learner {
T* _someRef;
public:
Learner() { _someRef = new T(); }
~Learner() {
if ( _someRef != nullptr ) {
delete _someRef;
_someRef = nullptr;
}
}
void caller() {
_someRef->doSomething();
}
};
class Base {
public:
virtual void doSomething() = 0;
};
class D1 : public Base {
public:
virtual void doSomething() override {
std::cout << "D1's doSomething was called" << std::endl;
}
};
class D2 : public Base {
public:
virtual void doSomething() override {
std::cout << "D2's doSomething was called" << std::endl;
}
};
int main() {
Learner<D1> ld1;
Learner<D2> ld2;
ld1.caller();
ld2.caller();
_getch();
return 0;
}
Here I didn't have to use any specializations. Now this pattern will probably only work with complete
types as well as those that are default
constructible. If they are not complete or default constructible that is where you will probably need to use specialization. For example:
Now let's see where specialization comes into play let's add a 3rd class with a non default constructor.
class D3 : public Base {
private:
unsigned int _x;
public:
explicit D3( unsigned int x ) : _x( x ) {}
virtual void doSomething() override {
std::cout << "D3's doSomething was called: constructed with " << _x << std::endl;
}
};
If we were to try and do this:
{
Learner<D3> ld3; // compiler error
Learner<D3> ld3( 5 ); // compiler error
Learner<D3> ld3( D3( 5 ) ); // compiler error
D3 d3(5);
Learnder<D3> ld3( d3 ); // compiler error
}
None of the above will work. So we HAVE to specialize as such:
template<>
class Learner<D3> {
private:
D3* _d3;
public:
explicit Learner( unsigned int x ) {
_d3 = new D3( x );
}
explicit Learner( D3 d3 ) {
_d3 = new D3( d3 );
}
~Learner() {
if ( _d3 != nullptr ) {
delete _d3;
_d3 = nullptr;
}
}
void caller() {
_d3->doSomething();
}
};
With this specialization I chose to add 2 constructors to this version of Learner<T>
. One for accepting a parameter to be able to construct a D3
object and one that takes a D3
object; now we can easily do this:
{
Learner<D3> ld3a( 7 );
ld3a.caller();
D3 d3( 5 );
Learner<D3> ld3b( d3 );
ld3b.caller();
}
I hope this helps you to understand the reasoning of specializations and or partial specializations with class templates. With default constructible complete types you don't need specializations but with non default types you do other wise they are considered incomplete types.
Depending on your code base you may not have to specialize
every sub class. It all depends on how they are constructed and if they are complete types.