I wanted do design a template class with two arguments that at compile time inherited based on the template arguments one of two mutually exclusive base classes.
I wanted to keep it simple for me so came up with this working example. The inheritance condition i got with std::conditional
based on the template arguments. The specialized methods for that conditional inheritance I set with std::enable_if
.
class Empty {};
template<typename T>
class NonEmpty { protected: std::vector<T> mObjects; };
template< typename A, typename B = A>
class Storage : public std::conditional<std::is_same<A, B>::value, Empty, NonEmpty<B>>::type
{
public:
template<typename C = B, typename std::enable_if<std::is_same<C, A>::value>::type* = nullptr>
void doStuff()
{
// one argument or two arguments with same type
// do stuff ...
};
template<typename C = B, typename std::enable_if<std::is_same<C, A>::value>::type* = nullptr>
void doSomthingElse()
{
// one argument or two arguments with same type
// do something exclusively just for this argument constellation ...
};
template<typename C = B, typename std::enable_if<!std::is_same<C, A>::value>::type* = nullptr>
void doStuff()
{
// two arguments with different types
// do stuff with inherited variables of NonEmpty-Class ...
};
};
int main()
{
EmptyClass<int> emp;
NonEmptyClass<int, float> nonemp;
emp.doStuff();
emp.doSomethingElse();
nonemp.doStuff();
}
Is there a better way to go about it, or are there any improvements for my existing solution? (I am using GCC 8.1.0 with C 14)
CodePudding user response:
In my opinion you're much better of partially specializing the template, since the entire implementation for both versions are completely independent. This way you can also not inherit any class instead of inheriting an empty class.
template<typename T>
class NonEmpty {
protected:
std::vector<T> mObjects;
};
template<typename A, typename B = A>
class Storage : public NonEmpty<B>
{
public:
void doStuff()
{
std::cout << "doStuff() different types\n";
};
};
template<typename A>
class Storage<A, A>
{
public:
void doStuff()
{
std::cout << "doStuff() same types\n";
};
void doSomethingElse()
{
std::cout << "doSomethingElse()\n";
};
};
int main() {
Storage<int> emp;
Storage<int, float> nonemp;
emp.doStuff();
emp.doSomethingElse();
nonemp.doStuff();
}
CodePudding user response:
I do not see the benefit of placing it all in one template. I have the impression that you are making things complicated by stuffing all in a single template but then need to select which version it is for each single method. Two seperate classes:
template <typename A, typename B>
struct inherits_from_empty : Empty {
// implement methods here, no sfinae needed
};
and
template <typename A, typename B>
struct inherits_from_nonEmpty : NonEmpty<A> {
// implement methods here, no sfinae needed
};
Can be choosen from via
template <typename A,typename B>
using Storage = std::conditional_t< put_condition_here<A,B> , inherits_from_empty<A>,inherits_from_nonEmpty<A>>;