Search code examples
c++variadic-templatestemplate-meta-programming

Call right template specialization for each type of a variadic template


I have a function foo() that takes a list of types T... and inside calls another (templated) function called do_stuff() for every element of a vector that is passed in. More specifically, we loop over the vector (of length sizeof...(T)), and would like to call do_stuff<Ti>() for vector[i], where Ti is the i'th type in T...

The information is available at compile time so I guess this is possible, but how we do it nicely?

#include <iostream>
#include <string>
#include <vector>
#include <cassert>

template <typename T>
T do_stuff(int param);

template <>
int do_stuff(int param)
{
    return int(100);
}

template <>
std::string do_stuff(int param)
{
    return std::string("foo");
}

template <typename... T>
void foo(const std::vector<int>& p)
{
    assert(p.size() == sizeof...(T));
    for (int i = 0; i < p.size(); ++i)
    {
        // Won't compile as T is not specified:
        //do_stuff(p[i]);
        // How do we choose the right T, in this case Ti from T...?
    }
}

int main()
{
    std::vector<int> params = { 0,1,0,5 };
    foo<int, std::string, std::string, int>(params);
}

Solution

  • You can use a C++17 fold expression:

    template <typename... T>
    void foo(const std::vector<int>& p)
    {
        assert(p.size() == sizeof...(T));
    
        std::size_t i{};
        (do_stuff<T>(p[i++]), ...);
    }
    

    live example on godbolt.org


    Alternatively, you can avoid the mutable i variable with std::index_sequence:

    template <typename... T>
    void foo(const std::vector<int>& p)
    {
        assert(p.size() == sizeof...(T));
    
        [&p]<auto... Is>(std::index_sequence<Is...>)
        {
            (do_stuff<T>(p[Is]), ...);
        }(std::index_sequence_for<T...>{});
    }
    

    live example on godbolt.org