c++ - 自动推断模板类型(带 lambda)
问题描述
我创建了以下辅助函数:
template<typename K1, typename V1, typename K2, typename V2>
[[nodiscard]] extern std::map<K2, V2> transform(const std::map<K1, V1> &map, std::function<std::pair<K2, V2>(K1, V1)> &&function) {
std::map<K2, V2> transformedMap{};
for (auto const &[k, v] : map) {
auto pair = function(k, v);
transformedMap.insert_or_assign(pair->first, pair->second);
}
return std::move(transformedMap);
}
使用它需要我明确声明所有类型:
auto mapped = transform<std::string, std::any, std::string, std::string>(map, [](const std::string &key, const std::any &value) {
return std::make_pair(key, std::any_cast<std::string>(value));
});
编译器有什么方法可以推断类型以便我可以编写:
auto mapped = transform(map, [](const std::string &key, const std::any &value) {
return std::make_pair(key, std::any_cast<std::string>(value));
});
解决方案
使用 lambda无法完成模板推导std::function
(另请参阅C++11 在涉及 std::function 或 lambda 函数时不推导类型)。
但是,您根本不需要 std::function
这里;您可以将function
其作为推导的模板类型模板参数,然后您可以对其进行约束,并使用它auto
来推导转换后的返回类型。
您只需要根据函数的结果来确定是什么K2
和V2
是什么——其结果可以通过 trait 检测到std::invoke_result_t
。如何确定什么K2
和V2
是由您决定——但如果您选择假设它function
总是会返回某种形式的std::pair
对象,这将非常容易,因为您可以从中访问first_type
和second_type
。
实际上,您可以根据需要使这部分变得复杂;例如,std::tuple_element_t
如果您想支持任何抽象对/类似元组的类型,您可以使用或其他东西。为简单起见,我将只演示前者。
如果您可以访问c++20,则可以通过以下std::invocable
概念解决:
#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;
}
如果您无权访问 C++20,则c++17解决方案是使用 SFINAE 和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 ...
注意:不要std::move
返回值——这是一种悲观,会阻止像复制省略这样的优化。
推荐阅读
- python - 测试功能获取多个文件
- php - 如何选择使用 Laravel Sanctum 进行身份验证?
- python - Kraken API - 帐户余额请求返回 Invalid Nonce
- javascript - 如何避免 .map 跳过空格?
- logback - java 17 logback SMTPAppender: AuthenticationFailedException: 220 准备启动 TLS
- openshift - RHEL dhcp 服务器配置 10.128.0.0/14 一些节点 CoreOS 正在获取 172.x ip 地址
- sql-server - 将两个源映射到目标 SQL DB 表
- twilio - 如何重复输入 DTMF 的值
- .net - 数千个后台作业的可扩展解决方案,每个作业都打开 Web 套接字
- azure - Azure 数据工厂:摄取 - 从 Delta 表到 Postgres