问题描述
场景 1:模板函数 pred
template<typename T>
bool pred(T t) { /* return a bool based on t */ }
场景 2:一组重载在同名 pred
上的函数
bool pred(A t) { /* return a bool based on t */ }
bool pred(B t) { /* return a bool based on t */ }
bool pred(C t) { /* return a bool based on t */ }
...
无论我们处于这两种情况中的哪一种,底线是 pred
不引用函数,因此它不能被传递,例如作为 std::remove_if
的一元谓词。
因此在这种情况下定义以下可以传递的对象很方便,
auto constexpr predobj = [](auto t){ return pred(t); };
但是,一旦我对另一个一元谓词有类似的需求,我需要复制并粘贴该行并将两个名称更改为其他名称;同样,如果我需要为二元谓词这样做:
auto contexpr binPredobj = [](auto x,auto y){ return binPred(x,y); };
auto funObj = fun2Obj(fun);
我觉得我问的是不可能的,因为它需要传递 fun
因为它是一个函数对象,它不是,否则我不需要制作一个函数对象其中。但问永远不会犯罪,对吧?
解决方法
你可以创建一个宏
#define FUNCTORIZE(func) [](auto&&... val) \
noexcept(noexcept(func(std::forward<decltype(val)>(val)...))) -> decltype(auto) \
{return func(std::forward<decltype(val)>(val)...);}
它将让您将任何可调用对象包装到一个闭包对象中。你会像这样使用它
auto constexpr predObj = FUNCTORIZE(pred);
,
这是一个执行适当的 SFINAE、noexcept 和转发的宏:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define CALLER_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(std::forward<decltype(args)>(args)...)) )
#define CALLER_OF_WITH_CAPTURE(...) \
[&](auto&&...args) \
RETURNS( __VA_ARGS__(std::forward<decltype(args)>(args)...)) )
auto constexpr predObj = CALLER_OF(pred);
已经有 a proposal 将其内置到语言中,如下所示:
auto constexpr predObj = (args...) => pred(std::forward<decltype(args)>(args)...);
但是被拒绝了。
CALLER_OF_WITH_CAPTURE
(和@Barry 的提议)有一个怪癖,即 noexcept/decltype 返回测试不在任何捕获的上下文中,而正文则在任何捕获的上下文中。所以 CALLER_OF_WITH_CAPTURE
可能会因此产生令人惊讶的错误。
在 CALLER_OF
中,参数可以是任何代币系列,但不平衡的结束 )
除外。
一段时间后,我与一位同事就这个话题进行了交谈,他让我知道有一个 Boost 库通过两个名为 BOOST_HOF_LIFT
and BOOST_HOF_LIFT_CLASS
的宏来提供这种功能。
示例:
#include <boost/hof.hpp>
#include <cassert>
#include <algorithm>
// Declare the class `max_f`
BOOST_HOF_LIFT_CLASS(max_f,std::max);
int main() {
auto my_max = BOOST_HOF_LIFT(std::max);
assert(my_max(3,4) == std::max(3,4));
assert(max_f()(3,4));
}
此外,here 是一篇关于该主题的精彩博文,其中也指向 Yakk - Adam Nevraumont 的回答所指向的同一提案。