Home > Enterprise >  C Restricting variable type to multiple base classes
C Restricting variable type to multiple base classes

Time:08-15

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>();
    ...
}
  • Related