可变参数模板,类型推导和标准::函数

Variadic templates, type deduction and std::function

本文关键字:标准 函数 类型 参数 变参      更新时间:2023-10-16

我正在尝试制作一个模板函数,可以将具有任何类型和数量的参数的其他函数传递给该函数并将其绑定到std::function。我设法做到了这一点:

#include <iostream>
#include <functional>
int foo(int bar)
{
  std::cout << bar << std::endl;
  return bar;
}
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
  return f;
}
int main()
{
  //auto barp = func(foo); // compilation error
  auto bar  = func(std::function<void (int)>(foo));
  bar (0); // prints 0
}

我只想调用auto barp = func(foo);并推断出类型,但这行给出了以下编译错误:

error: no matching function for call to ‘func(void (&)(int))’
    auto barp = func(foo);
                        ^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
 std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
                              ^
note:   template argument deduction/substitution failed:
note:   mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
   auto barp = func(foo);
                       ^

为什么它试图将std::function<_Res(_ArgTypes ...)>int (*)(int)相匹配?我觉得我应该以某种方式让编译器_Res(_ArgTypes ...)扩展到int(int),但是如何呢?

函数不是std::function,它可以转换为一个。但是,您可以推断函数的参数,除非对重载有歧义。

#include <iostream>
#include <functional>
int foo(int bar)
{
  std::cout << bar << std::endl;
  return 0;
}
// Will cause error.
//int foo(double); 
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
  return f;
}
int main()
{
  auto bar = func(foo);
  bar (0); // prints 0
}

你想用原始std::function做什么与此类似,更显然是行不通的:

template<typename T>
struct A
{
  A(T);  
};
template<typename T>
void func(A<T> a);
int main()
{
    func(42);
}

42不是A,但它可以转换为一个。但是,将其转换为一个需要已经知道T

你的代码在语义上等效于(如果编译的话(这个:

int foo(int x){
    std::cout << x << std::endl;
    return x;
}
int main(){
    auto bar = [](int x){ return foo(x); };
    bar(0);
}

除了return x部分,但这只是我纠正你未定义的行为。

返回std::function的函数是非常不必要的,除了可能为了减少键入。

您可以在没有包装器函数的情况下轻松使用 std::function 的构造函数。

但是,仍然。

要做你想做的事情,你应该传递函数指针本身;没有不可能的转换。

试试这个:

int foo(int x){
    return x + 1;
}
template<typename Ret, typename ... Args>
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{
    return {fp};
}
int main(){
    auto bar = func(foo);
    std::cout << bar(0) << std::endl; // outputs '1'
}

代码不起作用的原因是,当您将参数传递给 func 时,会尝试进行隐式转换。

正如我所说,您的代码目前在语义上等效于我上面使用 lambda 表达式显示的示例。我强烈建议在需要函数包装的地方使用 lambda 表达式!它们更加灵活,是语言的核心部分,而不是库功能。

请记住,非捕获 lambda 可以转换为函数指针;因此以下内容是一致的:

int main(){
    int(*bar)(int) = [](int x){ return x + 1; };
    std::cout << bar(0) << std::endl;
}

为了在帖子中拥有类似的功能,我们可以写这样的东西:

int main(){
    auto func = +[](int x){ return x + 1; };
    std::cout << "foo(5) = " << func(5) << std::endl;
    func = [](int x){ return x * 2; };
    std::cout << "bar(5) = " << func(5) << std::endl;
}

请注意,我们不会弄乱函数指针声明或库类型吗?每个人都能读/写更好。在此示例中要注意的一件事是一元+运算符;在将函数指针分配给变量之前执行到函数指针的转换。它实际上看起来非常实用,这似乎是您在这里要实现的目标。

尝试通过其operator()解开函子(lambda 和 std::function(的包装:

#include <iostream>
#include <functional>
int foo(int bar)
{
    std::cout << bar << std::endl;
    return 0;
}
template<typename /*Fn*/>
struct function_maker;
template<typename RTy, typename... ATy>
struct function_maker<RTy(ATy...)>
{
    template<typename T>
    static std::function<RTy(ATy...)> make_function(T&& fn)
    {
        return std::function<RTy(ATy...)>(std::forward<T>(fn));
    }
};
template<typename /*Fn*/>
struct unwrap;
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...) const>
    : function_maker<RTy(ATy...)> { };
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...)>
    : function_maker<RTy(ATy...)> { };
template<typename T>
auto func(T f)
    -> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>()))
{
    return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f));
}
int main()
{
    //auto barp = func(foo); // compilation error
    auto bar = func(std::function<void(int)>(foo));
    auto bar2 = func([](int)
    {
        // ...
    });
    bar(0); // prints 0
}

演示