Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a map function to a vector in C++11

Tags:

c++

c++11

I have a custom vector class that for all intents and purposes acts just like std::vector. I want to add a simple map function:

template <class T> class Vector
{
public:
    template<class mapFunction> Vector<typename mapFunction::result_type> map(mapFunction function)
    {
        Vector<mapFunction::result_type> result(_Length);
        for(UINT i = 0; i < _Length; i++)
        {
           result[i] = function(_Data[i]);
        }
        return result;
    }
    ...
}

Usage:

Vector<int> v(5);
for(int i = 0; i < 5; i++) v[i] = i;
auto mappedVector = v.map(function<double(int)>([](int a) { return a * 2.0; }));

This works but I'm trying to avoid the need for the cast from a lambda expression to std::function. Ideally it would just be v.map([](int a) { return a * 2.0; })); I realize that I could probably write a "make_function" similar to "make_pair" to avoid the need for template parameters, but you'd still need to cast all your lambdas.

I cast it to a std::function because I don't know how to extract the return type from a raw lambda type; hence I'm using std::function::result_type.

I thought the following would work, but it doesn't -- the compiler just complains that it cannot deduce the template argument for "returnType":

template<class mapFunction, class returnType> Vector<returnType> Map2(mapFunction function)
{
    Vector<returnType> result(_Length);
    for(UINT i = 0; i < _Length; i++)
    {
        result[i] = function(_Data[i]);
    }
    return result;
}

I realize that std::transform does this (I could easily replace the body of map with a call to std::transform), but my problem is really with the right way to specify the template parameters.

like image 815
Matt Fisher Avatar asked Dec 20 '22 00:12

Matt Fisher


1 Answers

First of all, don't use std::function for these kinds of problems.


I'll first give you an example, and I'll give a brief explanation. Note that I used an std::vector to store the data and provide the functionalities as I really don't want to implement the whole Vector class myself ;).

LIVE WORKING CODE

#include <iostream>
#include <vector>

//                /---------------------------------------------------------\
//                |                                                    //   |
template<typename T>                                                   //   |
class Vector {                                                         //   |
public:                                                                //   |
    std::vector<T> data;                                               //   |
                                                                       //   |
    template<class mapFunction>                                        //   |
    // Below: use C++11 trailing return type                                |
    auto map(mapFunction function) -> Vector<decltype(function(std::declval<T>()))>
    //                                       |                                   |
    {   //                                   |                                   |
        //                                   \-----------------------------------/
        //                                          |
        //                                          |
        //                 /-----------------------------------\
        //                 |                                   |
        using ReturnType = decltype(function(std::declval<T>()));

        Vector<ReturnType> result;

        auto size = data.size(); 
        result.data.reserve(size);

        for(std::size_t i = 0; i < size; i++)
        {
           result.data.push_back(function(data[i]));
        }

        return result;
    }
};

int main() {
    Vector<int> v;
    for(int i = 0; i < 10; ++i) {
        v.data.push_back(i);
    }

    auto newV = v.map([](int i) -> float {
        return (i * 2.0f) + 0.5f;       // Multiply by 2 and add 0.5
    });

    for(auto e : newV.data) {
        std::cout << e << std::endl;
    }
}

First, it uses C++11's trailing return type feature. It needs to be done this way because we need to reference the parameter function. The decltype(function(std::declval<T>())) part is the one who's interesting. In that, we basically ask the compiler

"What will be the return type of function given an argument value of type T?"

Then the compiler gives the return type and that's what we give to the first parameter of our result Vector.

The later parts have the same essence as yours, though I modified them for correctness and elegance.

Please note that in C++14, you could remove the trailing return type and just do

template<class mapFunction>
auto map(mapFunction function) {
   using ReturnType = decltype(function(std::declval<T>()));
   Vector<ReturnType> result;

   ...

   return result;
}
like image 167
Mark Garcia Avatar answered Jan 10 '23 00:01

Mark Garcia