Template deduction can't be done with std::function
using lambdas (see also C++11 does not deduce type when std::function or lambda functions are involved).
However you don't really need std::function
here at all; you could just take function
as a deduced template type-template argument, and then you can constrain it, and use auto
to deduce the converted return type.
You just need to determine what K2
and V2
are based on the result of the function -- the result of which can be detected with the trait std::invoke_result_t
. How you determine what K2
and V2
are is up to you -- but it can be quite easy if you choose to assume that function
will always return some form of std::pair
object, since you can just access the first_type
and second_type
from it.
Realistically, you can make this part as complex as you want; for example, you could use std::tuple_element_t
or something if you wanted to support any abstract pair/tuple-like type. For simplicity I will only demonstrate the former though.
If you have access to c++20 this can be solved with the std::invocable
concept:
#include <concepts> // for std::invocable
#include <type_traits> // for std::invoke_result_t
template<typename K, typename V, typename Fn>
[[nodiscard]]
auto transform(const std::map<K, V> &map, Fn&& function)
requires(std::invocable<Fn,K,V>)
{
using pair_type = std::invoke_result_t<Fn,K,V>;
using first_type = typename pair_type::first_type;
using second_type = typename pair_type::second_type;
auto transformedMap = std::map<first_type, second_type>{};
for (auto const &[k, v] : map) {
auto pair = function(k, v);
transformedMap.insert_or_assign(pair.first, pair.second);
}
return transformedMap;
}
Live Example
If you don't have access to C++20, then the c++17 solution would be to use SFINAE with std::is_invocable
:
#include <type_traits> // std::enable_if, std::is_invocable
template<typename K, typename V, typename Fn,
typename = std::enable_if_t<std::is_invocable_v<Fn,K,V>>>
[[nodiscard]]
... the rest is the same ...
Live Example
Note: Don't std::move
on return values -- this is a pessimization that will prevent optimizations like copy-elision.