Search code examples
c++templatesvariadic-templatestype-traits

Parameter pack aware std::is_base_of()


Is there a possibility to have a static assertion whether a type provided as template argument implements all of the types listed in the parameter pack ie. a parameter pack aware std::is_base_of()?

template <typename Type, typename... Requirements>
class CommonBase
{
    static_assert(is_base_of<Requirements..., Type>::value, "Invalid.");
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            parameter pack aware version of std::is_base_of()
public:
    template <typename T> T* as()
    {
        static_assert(std::is_base_of<Requirements..., T>::value, "Invalid.");
        return reinterpret_cast<T*>(this);
    }
};

Solution

  • Update for C++17: With C++17's fold expressions this becomes almost trivial:

    template <typename Type, typename... Requirements>
    class CommonBase
    {
        static_assert((std::is_base_of_v<Type, Requirements> && ...), "Invalid.");
    };
    

    Original Answer (C++11/14): You might use pack expansion and some static version of std::all_of:

    template <bool... b> struct static_all_of;
    
    //implementation: recurse, if the first argument is true
    template <bool... tail> 
    struct static_all_of<true, tail...> : static_all_of<tail...> {};
    
    //end recursion if first argument is false - 
    template <bool... tail> 
    struct static_all_of<false, tail...> : std::false_type {};
    
    // - or if no more arguments
    template <> struct static_all_of<> : std::true_type {};
    
    template <typename Type, typename... Requirements>
    class CommonBase
    {
        static_assert(static_all_of<std::is_base_of<Type, Requirements>::value...>::value, "Invalid.");
        //                                               pack expansion:      ^^^
    };
    
    struct Base {};
    struct Derived1 : Base {};
    struct Derived2 : Base {};
    struct NotDerived {};
    
    int main()
    {
      CommonBase <Base, Derived1, Derived2> ok;
      CommonBase <Base, Derived1, NotDerived, Derived2> error;
    }
    

    The pack expansion will expand to the list of values you get by inserting every type in Requirements... for the question mark in std::is_base_of<Type, ?>::value, i.e. for the first line in main it will expand to static_all_of<true, true>, for the second line it will be static_all_of<true, false, true>