Search code examples
c++templatesc++17c++20template-meta-programming

How to conditionally get template type of multiple base class with multiple inheritance


This is NOT a duplicate of link

Consider the following code:

#include <type_traits>

template <typename... Bases>
struct Overloads : public Bases... {};

template <typename T>
struct A {
  using AType = T;
};

template <typename T>
struct B {
  using BType = T;
};

template <typename T>
struct C {
  using CType = T;
};

template <typename OverloadsType>
struct Derived : public OverloadsType {
  
};

int main() {
    // OK
    static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>);
    // OK
    static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>);
    // OK
    static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>);
    // ???
    static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>);
    
}

Demo: Link

For the last line, I need to detect that Derived<Overloads<B<float>, C<char>>> is NOT derived from A, so I want typename Derived<Overloads<B<float>, C<char>>>::AType to be void or something (it fails to compile)

How can I do this?


Solution

  • My precise use case is: 1. Determine if Derived is derived from A<T> for some T. 2. If so, figure out that T.

    With C++20 concepts, this isn't too difficult. You need a function that takes A<T> as a parameter. There need not be a function definition; we're just using template argument deduction. It will never be called:

    template<typename T>
    T getDerivedFromAType(A<T> const&); //Not implemented, since we will never call it.
    

    Any type U for which getDerivedFromAType(u) works will either be an A<T> itself or a type derived from A<T> (from a single A<T> of course). So we can build a concept out of it:

    template<typename U>
    concept IsDerivedFromA = requires(U u)
    {
      getDerivedFromAType(u);
    };
    

    So long as nobody writes an overload of getDerivedFromAType, you're fine. This function ought to be in a detail namespace or have something else that lets people know that it is off-limits.

    To get the actual T used by A... well, there's a reason why the function returned T. We simply need a using statement that calculates the return type of the function call:

    template<IsDerivedFromA T>
    using BaseAType = decltype(getDerivedFromAType(std::declval<T>()));
    

    Notice that the template is guarded by our concept.