如何将 std::function 包装器转换为可变参数函数?

Posted

技术标签:

【中文标题】如何将 std::function 包装器转换为可变参数函数?【英文标题】:How do I convert a std::function wrapper into a variadic function? 【发布时间】:2017-10-05 21:00:54 【问题描述】:

我有这个非常好的包装器,但我希望它接受任意数量的 T、S、R、Q ......

template<typename U, typename T, typename S>
boost::variant<U, std::string> RunSafeFn2(const std::function<U(const T&,const S&)>& f, const std::string& errMsg, const  T& a1, const  S& a2)

    try
    
        return f(a1,a2);
    
    catch (...)
    
        return errMsg;
    

我尝试了以下方法并一直在谷歌搜索,但是错误消息很神秘 - 我正在尝试做的事情是否可能?

template<typename U, class ... Ts>
boost::variant<U, std::string> RunSafeFnN(const std::function<U(Ts)>& f, const std::string& errMsg, Ts ... ts)

    try
    
        return bind(f, ts...);
    
    catch (...)
    
        return errMsg;
    

【问题讨论】:

从 Clang 中,我得到 error: declaration type contains unexpanded parameter pack 'Ts',在 function&lt;U(Ts)&gt;Ts 下带有波浪线。如果我要判断,我会说这很清楚。 当然,但它告诉我该怎么做? 【参考方案1】:

你可以做你想做的事,正如这个简单的程序所示:

template <class ... Ts>
void foo(std::function<void(Ts...)> f, Ts && ... ts) 
    f(std::forward<Ts>(ts)...);


int main()

    std::function<void(int)> f = [] (int i)  std::cerr << "hello " << i; ;
    foo(f, 5);
    return 0;

问题是,一旦RunSafeFn2 是一个模板,你也可以模板化函子本身。当您已经是模板时,键入擦除可调用对象几乎没有什么好处。所以在实践中,这样做更有意义:

template <class F, class ... Ts>
void foo(F f, Ts && ... ts) 
    f(std::forward<Ts>(ts)...);

这仍然允许上面的用法,但也允许这样做:

foo([] (int i)  std::cerr << "hello " << i; , 5);

这也将更有效,因为您完全避免创建 std::function 对象。要处理返回类型,由于您仅限于 C++11,您可以这样做:

template <class F, class ... Ts>
auto foo(F f, Ts && ... ts) -> boost::variant<decltype(f(std::forward<Ts>(ts)...)), std::string> 
    try 
        return f(std::forward<Ts>(ts)...);
    
    ...

编辑:让我补充一个最后的想法:在 C++11 及更高版本中,可调用对象非常容易创建,一个更简单的替代方法实际上是采用可调用对象和 no 参数:

template <class F>
auto foo(F f) -> boost::variant<decltype(f()), std::string> 
    try 
        return f();
    
    ...

那是因为很容易捕捉到您需要的参数。例如,如果我以这种方式编写原始示例,我可以通过以下方式使用它:

int i = 5;
foo([&] ()  std::cerr << "hello " << i; );

这有利有弊,尤其是在处理仅移动类型时,但如果您想最大程度地减少维护负担并且您的用例很简单,这是一个合理的选择。

【讨论】:

好吧,bind 是一个完全的红鲱鱼。它用于从另一个创建一个可调用对象。但是您不想创建可调用对象,只想调用它。 std::forward 对于调用这样的可调用对象并不是绝对必要的,但由于各种原因,它是最正确的方法。 你能在 C++14 中为返回类型做一些更好的事情吗?我们在 MSVC 2015.2 是的,你的最终想法是我最初所做的,直到人们希望能够编写自己的失败包装器,并在失败包装器中包含参数。 @ProbablyAStupidQuestion 好吧,看看我的更新,总体上简化了很多事情。我实际上不确定 14 中是否有一个简单的简化;我本来想只使用 auto 但后来我意识到这与返回变体不太兼容。 它更优雅,但如果你通过 args,他们可以做一些聪明的事情,例如根据 args 检查返回。

以上是关于如何将 std::function 包装器转换为可变参数函数?的主要内容,如果未能解决你的问题,请参考以下文章

std::function"函数"对象包装器

包装器和绑定器std::bind和std::function的回调技术

包装器和绑定器std::bind和std::function的回调技术

如何将 std::function 对象传递给采用函数指针的函数?

[C++11]可调用对象包装器function

C++11新特性 :函数对象包装器