在函数对象中包装模板或重载函数的最简洁和可重用的方式

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);`