Consider the following classes:
class A {};
class B {};
class C {};
class D : public A, public B, public C {};
class E : public A, public C {};
class F : public A {};
I want to write a variable type that only accepts types which derive from both A
and B
(in this case only D
) so that the following hold:
T var;
var = D(); // valid
var = E(); // invalid, does not derive from B
var = F(); // invalid, does not derive from B
I could do the following and use G
as my type. G
would act as a sort of 'type grouper'.
class G : public A, public B {};
class D : public F, public C {};
However if I want to apply this to any combination of base types or with any number of base types, I would need a large number of these type groupers. Is there a way to implement a multiple base type requirement without manually generating type groupers like I showed?
I do not have a specific use case that requires this, I am purely curious whether this is possible.
EDIT: Accidentally used F
twice. Switched to G
for the second time.
CodePudding user response:
Similar to this answer, but using constraints and concepts in C 20.
#include <concepts>
class A {};
class B {};
class C {};
class D : public A, public B, public C {};
class E : public A, public C {};
class F : public A {};
template<class T>
concept AB = std::derived_from<T, A> && std::derived_from<T, B>;
template<AB T>
T *newAB() {
return new T;
}
int main() {
auto d = newAB<D>();
auto e = newAB<E>(); // error: 'E' does not satisfy 'AB'
auto f = newAB<F>(); // error: 'F' does not satisfy 'AB'
}
CodePudding user response:
If using C 11 can do this. Later versions offer some tweaks.
The closest I think you can get is using is_base_of and static_asserts, or a function which checks both with static_assert. e.g.
#include <type_traits>
class A {};
class B {};
class C {};
template <class T>
void CheckClassesAreDerivedFromAandB()
{
static_assert(std::is_base_of<A, T>::value, "Check we are derived from A");
static_assert(std::is_base_of<B, T>::value, "Check we are derived from B");
}
template <class T>
void CheckClassesAreDerivedFromAandB(const T&)
{
static_assert(std::is_base_of<A, T>::value, "Check we are derived from A");
static_assert(std::is_base_of<B, T>::value, "Check we are derived from B");
}
class D : public A, public B, public C {};
class E : public A, public C {};
class F : public A {};
void test()
{
CheckClassesAreDerivedFromAandB<D>();
D d;
CheckClassesAreDerivedFromAandB(d);
}
You could even combine this into a factory method.
template <class T>
T* Create()
{
CheckBaseClassesAreDerivedFromAandB<T>();
return new T();
}
template <class T>
T Build()
{
CheckBaseClassesAreDerivedFromAandB<T>();
return T();
}
void test()
{
auto d = Create<D>();
auto e = Create<E>(); // compile error here
auto another_d = Build<D>();
...
}