提问人:bitmask 提问时间:10/12/2022 更新时间:10/12/2022 访问量:155
函数模板是否可以在高阶函数调用中用作一等公民?
Can function templates be used as first class citizens in higher order function calls?
问:
将函数模板作为参数传递给另一个函数模板总是有点棘手。通常,必须求助于创建一个 lambda 对象来调用原始函数。
例
template <typename It>
void f(It, It) {}
void g(std::vector<int>::iterator, std::vector<int>::iterator) {}
template <typename C, typename F>
void callA(C&& c, F callable) {
return callable(std::begin(c), std::end(c));
}
问题
如果我有一个 ,我不能只传递给 因为是一个模板而不是一个函数:std::vector<int> c
f
callA
f
callA(c, f); // f has an unresolved overloaded function type and cannot be deduced
callA(c, std::distance); // same problem
callA(c, g); // works because g is a proper function, not a template
callA(c, [](auto a, auto b) {return f(a,b);}); // works again
即使我们帮助推断出可调用对象的类型:
template <typename C, template <typename> typename F,
typename T = std::decay_t<decltype(std::begin(std::declval<C>()))>>
auto callB(C&& c, F<T> callable) {
return callable(std::begin(c), std::end(c));
}
这无法编译。
问题
有没有办法强制 C++ 直接推断函数的类型,而无需诉诸 lambda 函数或类型擦除,例如?实际上,(如何)我才能将函数模板变成一等公民?也就是说,推断出它的类型。std::function
在定义高阶函数时,我愿意花一些时间(参见 vs),但在调用它时却不行。callB
callA
答:
3赞
songyuanyao
10/12/2022
#1
您可以将参数类型更改为函数指针,可以从函数模板转换(推导模板参数)。例如
template <typename C>
void callA(C&& c, void(*callable) (std::decay_t<decltype(std::begin(std::declval<C>()))>, std::decay_t<decltype(std::begin(std::declval<C>()))>)) {
return callable(std::begin(c), std::end(c));
}
然后
std::vector<int> c;
callA(c, f); // template argument It will be deduced as std::vector<int>::iterator for f
评论
0赞
n. m. could be an AI
10/12/2022
它不适用于非函数可调用对象,例如捕获 lambda 或 ,但这可以通过简单地将原始 OP 添加为重载来修复。std::function
callA
0赞
bitmask
10/12/2022
这似乎工作得很好,只是必须修复可调用对象的返回类型。因此,像 which return void 这样的函数需要与 .f
callA
std::distance
0赞
n. m. could be an AI
10/12/2022
@bitmask 这不是必需的,您可以为返回类型添加另一个模板参数
0赞
bitmask
10/12/2022
@n.1.8e9-where's-my-sharem。我无法让推论起作用。我可以让它工作,这样你就可以说和(默认为 )。但我看不出有办法自动开始工作。callA<std::ptrdiff_t>(a, std::distance)
callA(a, f)
void
callA(a, std::distance)
0赞
n. m. could be an AI
10/12/2022
@bitmask嗯,没错,推理是行不通的。
1赞
Enlico
10/12/2022
#2
你知道BOOST_HOF_LIFT
吗?
它允许您使用与 一样简单的语法将重载和/或模板化的函数提升到对象中。auto my_max = BOOST_HOF_LIFT(std::max);
您可以更改为 .callA(c, std::distance)
callA(c, BOOST_HOF_LIFT(std::distance))
评论
0赞
Caleth
10/12/2022
lift 是一个宏,用于定义模板周围的 lambda
0赞
Enlico
10/12/2022
@Caleth,我知道,但恕我直言,不必给自己写 lambda 是一件好事。我的意思是范围循环被脱糖成一个“正常”循环,但它不是一个有用的抽象吗?for
for
评论