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.
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 typeT
?"
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With