在模板函数中推导模板函数(使用 C++ 概念)
Deducing a template function within a template function (using C++ concepts)
此代码导致错误,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
是某种函数类型并使其也进行推导?
通过额外的重载,您可以让编译器解析 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:还有一些东西
Can I also make a templated function object work?
是的。 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); },
有效
基本上编译器已经从前 3 个参数中知道 T
是什么,因此它可以使用它来计算如何实例化 add
。为了更明确地说明我们希望从前 3 个参数而不是函数 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);
}
此代码导致错误,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; };
edit2:还有一些东西
Can I also make a templated function object work?
是的。 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); },
有效
基本上编译器已经从前 3 个参数中知道 T
是什么,因此它可以使用它来计算如何实例化 add
。为了更明确地说明我们希望从前 3 个参数而不是函数 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);
}