Search code examples
templatesc++14variadic-templates

Calling base function from variadic template class in c++14


I have class structure as following. How can I call functions from MultiBase class for all Base classes. Is there any way to iterate over the arguments in parametr pack ?

template <typename T, typename... Args>
class Base : public Base<T>{
};

template <typename T>
class Base<T> {

    protected:
       void test()
       {
         std::cout << typeid(m_data).name() << std::endl;
       }
       void insert(T data)
       {
           m_data = data;
       }
    private:
      T m_data;
};

template<typename T, typename ...Args>
class MultiBase : public Base<T, Args...>
{
    public:
    template<typename V>
    void insert(ClientId key, V data)
    {
        Base<V>::insert(key, data);
    }

    void f()
    {
        // call test() from Base class for all types from parameter pack
    }
};


int main()
{
    MultiBase<int,float> ms;
    ms.f() // equivalent to calling Base<int>::test() and Base<std::string>::test()
    return 0;
}

Thanks in advance for help.


Solution

  • Your code has many issues, for example MultiBase class only has a single base. But to answer the immediate question: you can use pack expansion. I've provided two examples: expansion in a brace-enclosed initializer list and expansion in a template argument list, and a bonus one for c++17.

    #include <iostream>
    #include <type_traits>
    
    template <typename Value>
    class Base {
    protected:
      void test() {
        std::cout << typeid(Value).name() << std::endl;
      }
    };
    
    template <typename... Values>
    class MultiBase : public Base<Values>... {
    public:
      void f() {
        // hacky solution:
        int unused[]{(Base<Values>::test(), 0)...};
    
        // generic recursive solution:
        f_impl<Values...>();
    
        // c++17 simplifies this with fold-expressions:
        // (Base<Values>::test(), ...);
      }
    
    private:
      template <typename First, typename... Others>
      void f_impl() {
        Base<First>::test();
        f_impl<Others...>();
      }
      
      // overload to terminate recursion,
      // SFINAE to only be eligible for empty parameter pack
      template <typename... Others, typename = std::enable_if_t<sizeof...(Others) == 0>>
      void f_impl() {
      }
    };
    
    int main() {
      MultiBase<int, float, const char*> ms;
      ms.f();
      return 0;
    }