How to generalize the functions, which acting on a vector of different types?

Viewed 157

I am relatively new to C++ and I can not seem to phrase my question correctly. Since vectorize and map both have meanings which seem different from what I am looking for (so Googling for a solution is difficult).

I have many functions acting on single objects of arbitrary type. I want a general way (i.e. a template or wrapper function) to convert these to functions acting on vectors of the arbitrary number of objects of the same type.

An example:

int func1(int a) {return a+1;}
long func2(long a) {return a*10;}

vector<int> func1(vector<int> a_vec)
{
    vector<int> out_vec;
    for (int i = 0; i < a_vec.size(); i++)
        out_vec.push_back(func1(a_vec[i]));
    return out_vec;
}

vector<long> func2(vector<long> a_vec)
{
    vector<long> out_vec;
    for (int i = 0; i < a_vec.size(); i++)
        out_vec.push_back(func2(a_vec[i]));
    return out_vec;
}

Templates seem to be necessary but I don't have much experience with them yet and have no idea how to apply them in this case. Any references/suggestions/comments welcome.

(Also please add relevant tags - since I am not sure of the correct term for this I am not sure how to tag it).

3 Answers

This code does indeed look like a nice fit for some templates.

template <typename T, typename TFunc>
std::vector<T> transform(const std::vector<T> &v, TFunc &&func)
{
    std::vector<T> result;
    result.reserve(v.size());
    for (auto &&element : v)
        result.push_back(std::invoke(func, element));
    return result;
}

As you might notice the function looks very similar to std::transform, which works on iterators instead of vectors. It can be called as:

 auto result = transform(v, &func1);

As @paler123 mentioned in the comment, you can use std::transform to do the job + a small templated function.

Since your two functions have similarities in the function signature

i.e, Type function(Type arg)

I chose, typed function pointers as the template function parameters.

template<typename Type>
std::vector<Type> func(const std::vector<Type>& a_vec, Type(*func)(Type))
{
    std::vector<Type> out_vec; out_vec.reserve(a_vec.size());
    std::transform(a_vec.begin(), a_vec.end(), std::back_inserter(out_vec), func);
    return out_vec;
}

Now in the main(), you can either specify the type explicitly or leave as it is and let the compiler do the type deduction.

auto result = func<int>({ 1,2,3,4 }, func1);
// also can
auto result2 = func({ 1, 2, 3, 4 }, func2);

See sample output here

Yes, you will need templates since you don't know the types that you'll be using this vectorization function with.

Let's try it:

#include <vector>
#include <iostream>

int func1(int i){ return i + 1; }

template<typename valueT, typename callableT>
std::vector<valueT> vectorize(const std::vector<valueT>& input, callableT funcToCall)
{
    std::vector<valueT> result;
    for (auto& x : input) result.push_back(funcToCall(x));
    return result;
}


int main()
{
    std::vector<int> iVector{2,3,5,7,11,13,17};

    std::vector<int> output = vectorize(iVector, &func1);

    for (const auto& x : output) std::cout << x << std::endl;

    return 0;
}
Related