【问题标题】:Variadic templates, type deduction and std::function可变参数模板、类型推导和 std::function
【发布时间】:2015-12-09 22:00:12
【问题描述】:

我正在尝试创建一个模板函数,可以向该函数传递具有任何类型和数量的参数的其他函数并将其绑定到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&lt;_Res(_ArgTypes ...)&gt;int (*)(int) 匹配?我觉得我应该让编译器以某种方式将_Res(_ArgTypes ...) 扩展为int(int),但是如何?

【问题讨论】:

  • 你为什么要转换成std::functionstd::function 是一个类型擦除类:它获取构造它的类型信息,并擦除大部分。类型推导获取它的参数,并推导它的类型,并生成代码。您要求推断将某些内容删除的类型。这就像制作一套盔甲,可以制造出向穿戴者射击的枪;在大多数意义上,类型擦除与类型推导相反。这很少是一个好主意。有可能,但 C++ 没有理由让它简单你有实际用例吗?
  • 重载或变量参数列表怎么样?只有在少数情况下才有可能。不过你可以使用 boost function_traits 之类的东西。
  • @Yakk,我试图编写一个“memoization”函数,因为它将返回一个具有相同参数的可调用对象并返回其参数的类型,该参数将调用原始函数,除了它将查找以前的计算值(存储在合适的地图中)。我会使用std::function 来存储原始函数。不确定这是否可能。
  • @Tarc 为什么不只存储原始的可调用对象?请注意,函数名称可以具有重载,并且可调用对象可以具有一组以上的合法参数。请参阅here 了解仅适用于函数指针的单参数记忆器。 here 是一个更通用的递归处理记忆器。

标签: c++ c++11 variadic-templates std-function


【解决方案1】:

函数不是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,不过它可以转换为一个。但是,将其转换为 1 需要已知 T

【讨论】:

  • &amp;foo 有点无关紧要。调用表达式之外的函数名在 C/C++ 中被隐式转换为函数指针。
  • @CoffeeandCode,是的,改变了这一点。我更喜欢它,因为它是成员函数所需要的。但这只是一个额外的复杂性。
【解决方案2】:

您的代码在语义上等同于(如果已编译):

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::functions 构造函数而不需要包装函数。

但是,仍然。

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

试试这个:

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;
}

注意我们不会乱用函数指针声明或库类型?每个人都可以更好地读/写。 One thing to notice in this example is the unary + operator; to perform a conversion to function pointer before assigning it to the variable. 它实际上看起来非常功能,这似乎是你想要在这里实现的目标。

【讨论】:

    【解决方案3】:

    尝试通过 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
    }
    

    Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多