c++ - 在模板函数中推导模板函数(使用 C++ 概念)
问题描述
此代码导致错误,error: no matching function to call to 'put'
.
#include <concepts>
#include <type_traits> // for is_invocable_r_v
#include <iostream>
template <typename T>
T add(T a, T b)
{
return a + b;
}
template <typename T, typename F>
requires std::is_invocable_r_v<T, F, T, T>
void put(T& r, T a, T b, F f)
{
r = f(a, b);
}
int main()
{
int x;
put(x, 1, 2, add);
std::cout << "x = " << x << '\n';
}
这似乎是因为概念无助于推导模板参数,尽管它们可以约束它。
我可以put
使用函数指针而不是引入参数来定义F
。但是,它将排除更通用的函数对象,例如(捕获)用于f
.
template <typename T>
void put(T& r, T a, T b, T (f)(T, T))
{
r = f(a, b);
}
当然,我可以更具体一点put(x, 1, 2, add<int>)
,它适用于这个简单的示例。但是,在更通用的情况下,比如编写一个库,我希望编译器为我完成它并add
从外部上下文中确定正确的实例。
是否可以让编译器知道F
是某种功能类型并使其推断出来?
解决方案
通过额外的重载,您可以让编译器解析add
并且您仍然支持函数对象。
template <typename T>
T add(T a, T b)
{
return a + b;
}
template <typename T, typename F>
requires std::is_invocable_r_v<T, F, T, T>
void put(T& r, T a, T b, F f)
{
r = f(a, b);
}
template <typename T>
void put(T& r, T a, T b, T(f)(T,T))
{
r = f(a, b);
}
auto template_lambda_add = []<class T>(T a, T b) -> T { return a + b; };
int main()
{
int x;
put(x, 1, 2, add);
std::cout << "x = " << x << '\n';
put(x, 1, 2, std::plus<void>{});
std::cout << "x = " << x << '\n';
put(x, 1, 2, [](auto a, auto b){return a+b;});
std::cout << "x = " << x << '\n';
put(x, 1, 2, template_lambda_add);
std::cout << "x = " << x << '\n';
}
编辑:
还支持模板 lambda。我添加了一个示例来演示。
作为附加说明,模板 lambda 的语法如下
auto lambda_add = []<class T>(T a, T b) -> T { return a + b; };
类似下面的内容将是一个模板化的变量声明。即用于创建 lambda 的模板,而不是模板化的 lambda。
template <class T>
auto lambda_add = [](T a, T b) -> T { return a + b; };
编辑2:更多的东西
我也可以使模板化的函数对象工作吗?
是的。std::plus<void>
基本上就是这样。
您必须模板化运算符而不是结构。例如
struct Add {
template <class T>
T operator()(T a, T b) { return a + b; }
};
以及为什么过载template <typename T> void put(T& r, T a, T b, T(f)(T,T)) { r = f(a, b); },
有效
基本上编译器已经知道T
前 3 个参数是什么,所以它可以用它来确定如何实例化add
. 为了更明确地说明我们希望T
从前 3 个参数而不是函数 ptr 参数中推导出来,我们可以std::type_identity
像这样使用
template <typename T>
void put(T& r, T a, T b, std::type_identity_t<T(T,T)> f)
{
r = f(a, b);
}
推荐阅读
- ruby - 按散列属性的值按降序对散列数组进行排序,保留散列的初始顺序
- autodesk-forge - GET hubs 请求返回一些但不是所有必需的集线器
- python - 如果没有图片,如何在同一位置刮掉多个图像的链接空白该位置
- javascript - 根据数组对象中存在的键排序
- bash - 替代品如何比使用 curl | 更好 须藤
- ios - 配置文件和签名问题
- c# - 反序列化复杂类型,但将选定的属性保持为序列化
- delphi - 如何将动态文本写入导航栏使用的 TImageList 中的图像?
- javascript - 初学者学习javascript
- c# - 如何设置图像目标以检测同一图像目标的所有尺寸