I am learning about C++ right now, and trying out some of the templating features. I am trying to create a generic template that receives a function F
from U
to V
, a std::array
of type U
, and then returns (via NRVO) an array of type V
.
This is what I came up with on a first pass. It seems reasonable to my untrained eye, but the compiler doesn't like it:
template <typename F, typename U, typename V, std::size_t N> std::array<V, N> map(F mapper, const std::array<U, N> &elements)
{
std::array<V, N> newArray{};
for (std::size_t i = 0; i < N; i++) {
newArray[i] = mapper(elements[i]);
}
return newArray;
}
// ...
int main()
{
std::array<int, 10> ints1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto doubles1 = map([](int x) -> double { return x*2.5; }, ints1);
}
I am using clang 16.0.6, and it tells me:
functional.cpp:87:19: error: no matching function for call to 'map'
auto doubles1 = map([](int x) -> double { return x*2.5; }, ints1);
^~~~~~~~~~~~
functional.cpp:22:79: note: candidate template ignored: couldn't infer template argument 'V'
template <typename F, typename U, typename V, std::size_t N> std::array<V, N> map(F mapper, std::array<U, N> &elements)
^
1 error generated.
To my mind, it seems like there should be something I can do to hint to the compiler that the lambda returns a double, and hence the auto
should be deduced as std::array<double, N>
. Is there something easy I am missing? Thank you.
I expected the code to compile of course, but something is not quite right. I am still pretty new to C++ lingo, so I am having trouble knowing what exactly to google to resolve this. One possible solution I came up with was to use an out-parameter:
template <typename F, typename U, typename V, std::size_t N> void map_outParam(F mapper, std::array<U, N> &elements, std::array<V, N> &out)
{
for (std::size_t i = 0; i < N; i++) {
out[i] = mapper(elements[i]);
}
}
This compiles just fine, and works as expected. However, it's not as clean as I would like.
Basically, you can just use the result type of your callback function to figure out the type for the result array.
#include <type_traits>
#include <array>
template <typename F, typename U, std::size_t N> auto map(F mapper, std::array<U, N> &elements)
{
std::array<std::invoke_result_t<F, U>, N> newArray{};
for (std::size_t i = 0; i < N; i++) {
newArray[i] = mapper(elements[i]);
}
return newArray;
}
// ...
int main()
{
std::array<int, 10> ints1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto doubles1 = map([](int x) -> double { return x*2.5; }, ints1);
}