简体   繁体   English

变体模板,类型演绎和std :: function

[英]Variadic templates, type deduction and std::function

I'm trying to make a template function to which is possible to pass some other function with any type and number of parameters and bind it to a std::function . 我正在尝试创建一个模板函数,可以通过任何类型和数量的参数传递一些其他函数,并将其绑定到std::function I managed to do this: 我成功地做到了这一点:

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

I would like to just call auto barp = func(foo); 我想调用auto barp = func(foo); and have the types deduced, but this line gives the following compilation errors: 并推导出类型,但此行给出以下编译错误:

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);
                       ^

Why is it trying to match std::function<_Res(_ArgTypes ...)> with int (*)(int) ? 为什么尝试将std::function<_Res(_ArgTypes ...)>int (*)(int)匹配? I feel I should get the compiler somehow to expand _Res(_ArgTypes ...) to int(int) , but how? 我觉得我应该以某种方式让编译器将_Res(_ArgTypes ...)扩展为int(int) ,但是如何?

A function is not an std::function , it is convertible to one. 函数不是std::function ,它可以转换为一个。 You can deduce the arguments of a function, however, barring ambiguity about overloads. 但是,您可以推断出函数的参数,除非有关重载的歧义。

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

What you want to do with the original std::function is similar to this, which more obviously does not work: 你想用原始的std::function做什么与此类似,这显然不起作用:

template<typename T>
struct A
{
  A(T);  
};

template<typename T>
void func(A<T> a);

int main()
{
    func(42);
}

42 is not a A , it can be converted to one, though. 42不是A ,但它可以转换成一个。 However, converting it to one would require T to already be known. 但是,将其转换为一个将需要已知T

Your code is semantically equivalent to (if it compiled) this: 您的代码在语义上等效于(如果已编译)此代码:

int foo(int x){
    std::cout << x << std::endl;
    return x;
}

int main(){
    auto bar = [](int x){ return foo(x); };
    bar(0);
}

Apart from the return x part, but that's just me correcting your undefined behaviour. 除了return x部分,但这只是我纠正你未定义的行为。

Your function for returning an std::function is extremely unnecessary, except maybe for the sake of less typing. 返回std::function是非常不必要的,除非为了减少输入。

You could just as easily use std::function s constructor without the wrapper function. 您可以在没有包装函数的情况下轻松使用std::function的构造函数。

But, still. 但是,仍然。

To do what you want to do, you should pass the function pointer itself; 要做你想做的事,你应该传递函数指针本身; without impossible conversion. 没有不可能转换。

try this: 试试这个:

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

The reason your code doesn't work is because of the implicit conversion trying to take place when you pass an argument to func . 您的代码不起作用的原因是因为在将参数传递给func时尝试进行隐式转换。

As I said, your code currently is semantically equivalent to the example I showed above using lambda expressions. 正如我所说,您的代码目前在语义上等同于我上面使用lambda表达式显示的示例。 I would strongly recommend just using lambda expressions wherever function wrapping is needed! 我强烈建议只在需要函数包装的地方使用lambda表达式! They're much more flexible and are a core part of the language rather than a library feature. 它们更灵活,是语言的核心部分,而不是库功能。

Remember, non-capturing lambdas are convertible to function pointers; 请记住,非捕获lambda可以转换为函数指针; so the following is conforming: 所以以下是符合:

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

and to have similar functionality as you want in your post, we could write something like this: 并且在你的帖子中有你想要的类似功能,我们可以写这样的东西:

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

Notice we don't mess around with function pointer declarations or library types? 请注意,我们不会乱用函数指针声明或库类型? Much nicer for everybody to read/write. 每个人读/写都好多了。 One thing to notice in this example is the unary + operator; 在这个例子中要注意的一件事是一元+运算符; to perform a conversion to function pointer before assigning it to the variable. 在将其指定给变量之前执行到函数指针的转换。 It actually looks very functional , which seems to be what you're trying to achieve here. 它实际上看起来很实用 ,这似乎是你想要在这里实现的。

Try to unwrap the functor (lambda and std::function) through its operator() : 尝试通过其operator()展开仿函数(lambda和std :: function operator()

#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 演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM