如何将多个 std::function 合并为一个?

How To Combine Multiple std::function To One?

我有多个 std::function。它们每个都有不同的输入和输出,并且一个 std::function 的输入可能是另一个 std::function 的输出,这意味着 "serial" 从一个转换到另一个。

可能我描述的不够清楚。让代码说话

std::function<bool(double)> combine(std::function<int(double)> convert1
                                    , std::function<char(int)> convert2
                                    , std::function<bool(char)> convert3)
{
    return std::bind(convert1, convert2, convert3)//error. A function directly convert [double] to [bool] using convert1, convert2, convert3
}

这是非常简单的代码,我已经删除了无意义的代码并显示了我的核心意思。

因此您可以看到 convert1double 转换为 intconvert2int 转换为 charconvert3 做从 charbool 的转换。现在我需要将它们组合在一起,以便我可以直接将 double 转换为 bool

而且你知道,我并不是真的想将 double 转换为 bool。仅供测试。

实现这个的一个选择是编写一个帮助函数:

bool helper(double d
            , std::function<int(double)> convert1
            , std::function<char(int)> convert2
            , std::function<bool(char)> convert3)
{
    return convert3(convert2(convert1(d)));
}

std::function<double(bool)> combine(std::function<int(double)> convert1
                                    , std::function<char(int)> convert2
                                    , std::function<bool(char)> convert3)
{
    return helper;
}

但它是丑陋的代码,也许我以一种常见的方式使用这种转换,这意味着我应该为所有类型的转换编写这种 helper

那么,有没有一种直接将这些功能组合在一起的方法呢?

您可以使用 lambda 表达式来执行此操作。


std::function<bool(double)> combine(std::function<int(double)> convert1
                                    , std::function<char(int)> convert2
                                    , std::function<bool(char)> convert3)
{
    return [=](double d){return convert3(convert2(convert1(d)));}
}

或者你可以直接在代码中使用 lambda,完全不使用这个 combine 函数,这样会更清楚发生了什么。


如果您仍然想使用 combine 函数并想要更通用的函数,也许您可​​以尝试这样的方法。 (只是一个简单的例子)

template<typename Converter>
auto combineX(Converter converter){
   return converter;
}

template<typename Converter, typename ...Converters>
auto combineX(Converter converter, Converters... converters){
   return [converter,remain = combineX(converters...)](auto x){return remain(converter(x));};
}

创建一个简单的类型特征来提取最后一个函数的输入类型

template <typename, typename...>
struct lastFnType;

template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
 { using type = typename lastFnType<F1, Fn...>::type; };

template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
 { using type = T1; };

你可以将apple apple的解决方案(+1)转化为更通用的可变参数模板递归解决方案

template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
 { return conv; }

template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
    combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2, 
             Fn ... fn)
 {
   using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;

   return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
 }

但是观察转换器的顺序是颠倒的(首先调用最后使用的转换器;所以combine(convert3, convert2, convert1)

下面是一个完整的例子

#include <functional>

template <typename, typename...>
struct lastFnType;

template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
 { using type = typename lastFnType<F1, Fn...>::type; };

template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
 { using type = T1; };

template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
 { return conv; }

template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
    combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2, 
             Fn ... fn)
 {
   using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;

   return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
 }

int fn1 (double d)
 { return d*2.0; }

char fn2 (int i)
 { return i+3; }

bool fn3 (char c)
 { return c == 'a'; }


int main ()
 {
   std::function<int(double)> f1 { fn1 };
   std::function<char(int)> f2 { fn2 };
   std::function<bool(char)> f3 { fn3 };

   auto cmb = combine(f3, f2, f1);

   bool b { cmb(3.2) };
 }

你可以这样做:

template <typename T, typename F>
decltype(auto) apply(T&& t, F&& f)
{
    return std::forward<F>(f)(std::forward<T>(t));
}

template <typename T, typename F, typename... Fs>
decltype(auto) apply(T&& t, F&& f, Fs&&... fs)
{
    return apply(std::forward<F>(f)(std::forward<T>(t)), std::forward<Fs>(fs)...);
}

使用情况:

apply(4,
      [](int i) { return i * 10; },
      [](auto i) {return i + 2;},
      [](auto n){ return n / 10.f; })

Demo