此代码会导致错误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
的lambda。
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
是某种函数类型并使其推断出来?
答案 0 :(得分:1)
由于额外的重载,编译器可以解析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; };
edit2:更多内容
是的。基本上就是我还可以使模板化的函数对象起作用吗?
std::plus<void>
。
您必须使用模板作为运算符,而不是struct。例如
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
是什么,因此编译器可以使用它来找出如何实例化add
。为了更明确地说明我们希望从前三个参数而非函数ptr参数推导出T
的事实,我们可以像这样使用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);
}