K.K. Slider
K.K. Slider

Reputation: 77

How to pass a vector (or similar) into a variadic template

Suppose I have the following code:

template <typename... Args>
void DoSomething(const Args&... args)
{
    for (const auto& arg : {args...})
    {
        // Does something
    }
}

Now let's say I'm calling this from another function, and want to pass in an std::vector (or somehow modify the vector in such a way that it could be used with this)

void DoSomethingElse()
{
    // This is how I'd use the function normally
    DoSomething(50, 60, 25);

    // But this is something I'd like to be able to do as well
    std::vector<int> vec{50, 60, 25};
    DoSomething(??); // <- Ideally I'd pass in "vec" somehow
}

Is there anyway to do this? I've also considered using std::initializer_list instead of variadic templates, but the issue still remains that I have no way to passing in existing data.

Thank you.

Upvotes: 5

Views: 2818

Answers (2)

super
super

Reputation: 12928

Here is one approach that uses SFINAE. Pass one element and it will be assumed it's something that works in a ranged for-loop.

If you pass several arguments it constructs a vector and iterates over that.

#include <iostream>
#include <type_traits>
#include <vector>

template <typename... Args, typename std::enable_if<(sizeof...(Args) > 1), int>::type = 0>
void DoSomething(const Args&... args)
{
    for (auto& a : {typename std::common_type<Args...>::type(args)...})
    {
        cout << a << endl;
    }
}

template <typename Arg>
void DoSomething(Arg& arg)
{
    for (auto a : arg)
    {
        std::cout << a << std::endl;
    }
}

int main() {
    DoSomething(10, 50, 74);

    std::vector<int> foo = {12,15,19};
    DoSomething(foo);
    return 0;
}

Upvotes: 3

jfMR
jfMR

Reputation: 24738

Assuming the syntax DoSomething({50, 60, 25}) is acceptable, you could first write a non-variadic function template for containers:

template <typename T>
void DoSomething(const T& coll) 
{
    for (const auto& arg : coll) {
        // ...
    }
}

Then, a non-variadic function template for std::initializer_list<>:

template<typename T>
void DoSomething(const std::initializer_list<T>& lst)
{
    for (const auto& elem: lst) {
       // ...
    }
}

They can be used this way:

void DoSomethingElse()
{
    std::vector<int> vec{50, 60, 25};
    std::list<int> lst{50, 60, 25};

    // 1st function template
    DoSomething(vec);
    DoSomething(lst);

    // 2nd function template
    DoSomething({50, 60, 25});
}

In order to avoid code duplication, the second function template could create a std::vector from the std::initializer_list argument and then call the other function template with that vector:

template<typename T>
void DoSomething(const std::initializer_list<T>& lst)
{
    std::vector<T> vec(lst);
    DoSomething(vec);
}

Upvotes: 2

Related Questions