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

问题描述

场景 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 的回答所指向的同一提案。