在函数对象中包装模板或重载函数的最简洁和可重用的方式
Most terse and reusable way of wrapping template or overloaded functions in function objects
场景 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 return 测试是在 不在任何捕获的上下文中完成的 ,而主体 在任何捕获的上下文中完成 。所以 CALLER_OF_WITH_CAPTURE
可能会因此产生令人惊讶的错误。
在 CALLER_OF
中,参数可以是任何系列的标记,不平衡的关闭 )
.
除外
过了一段时间,我和一位同事就这个话题聊了聊,他让我知道有一个 Boost 库通过两个名为 BOOST_HOF_LIFT
and BOOST_HOF_LIFT_CLASS
.[=20= 的宏提供了这个功能]
示例:
#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) == std::max(3, 4));
}
此外,here 是一篇关于该主题的精彩博客 post,它也指向 Yakk - Adam Nevraumont 的回答所指向的相同提案。
我的问题是关于模板化 and/or 重载函数,因为这是您不能传递的“命名事物”的“类别”。
不过,还有一类:构造函数。例如,您不能将构造函数作为运算符传递给 std::transform
算法。
嗯,Boost.Hof 在这方面也有帮助,通过 boost::hof::construct
模板,assert(construct<T>()(xs...) == T(xs...));
成立。
可以这样用
#include <boost/hof/construct.hpp>
struct A {
A(int) {} // some...
A(int, int) {} // ... constructors
};
auto makeA = boost::hof::construct<A>();
// now `makeA` is the "objectified" version of `A`, so you could use it like this:
std::transform(vecOfInts.begin(), vecOfInts.end(), vecOfAs.begin(), makeA);`
场景 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 return 测试是在 不在任何捕获的上下文中完成的 ,而主体 在任何捕获的上下文中完成 。所以 CALLER_OF_WITH_CAPTURE
可能会因此产生令人惊讶的错误。
在 CALLER_OF
中,参数可以是任何系列的标记,不平衡的关闭 )
.
过了一段时间,我和一位同事就这个话题聊了聊,他让我知道有一个 Boost 库通过两个名为 BOOST_HOF_LIFT
and BOOST_HOF_LIFT_CLASS
.[=20= 的宏提供了这个功能]
示例:
#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) == std::max(3, 4));
}
此外,here 是一篇关于该主题的精彩博客 post,它也指向 Yakk - Adam Nevraumont 的回答所指向的相同提案。
我的问题是关于模板化 and/or 重载函数,因为这是您不能传递的“命名事物”的“类别”。
不过,还有一类:构造函数。例如,您不能将构造函数作为运算符传递给 std::transform
算法。
嗯,Boost.Hof 在这方面也有帮助,通过 boost::hof::construct
模板,assert(construct<T>()(xs...) == T(xs...));
成立。
可以这样用
#include <boost/hof/construct.hpp>
struct A {
A(int) {} // some...
A(int, int) {} // ... constructors
};
auto makeA = boost::hof::construct<A>();
// now `makeA` is the "objectified" version of `A`, so you could use it like this:
std::transform(vecOfInts.begin(), vecOfInts.end(), vecOfAs.begin(), makeA);`