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.