2015-09-13 103 views
5

我试图制作一个模板函数,可以通过任何类型和数量的参数传递一些其他函数,并将其绑定到std::function。我成功地做到这一点:Variadic模板,类型演绎和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),但如何?

+4

你为什么要转换为'的std :: function'呢? 'std :: function'是一个类型擦除类:它从它构造的类型信息中获取,并擦除大部分内容。类型推断采用它的参数,并推导出它的类型,并生成代码。你要求推断出什么类型的东西要删除。这就像制作一套盔甲来制造枪支射向佩戴者;类型删除是大多数意义上的类型演绎的*对立*。这很少是一个好主意。这是可能的,但C++没有理由让它变得容易*你有一个实际的用例吗? – Yakk

+0

重载或可变参数列表如何?只有在少数情况下才有可能。尽管你可以使用诸如boost function_traits之类的东西。 – tahsmith

+0

@Yakk,我试图编写一个“memoization”函数,就是说它会返回一个带有相同参数的可调用函数,并且它的参数的返回类型会调用原始函数,除了它会查找以前的计算值(存储在合适的地图中)。我会使用'std :: function'来存储原始函数。不知道这是否可能。 – Tarc

回答

2

函数不是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已知。

+0

'&foo'有点无关紧要。调用表达式之外的函数名称将隐式转换为C/C++中的函数指针。 – CoffeeandCode

+0

@CoffeeandCode,是的,改变了。我更喜欢它,因为它是成员函数所必需的。但是,这只是一个额外的复杂性。 – tahsmith

1

您的代码在语义上等效(如果编译)这样的:

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表达式!它们更灵活,是语言的核心部分,而不是库功能。记住,非捕获的lambdas可转换为函数指针;所以下面是符合:

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.它实际上看起来很功能,这似乎是你想在这里实现的。

1

试图通过其operator()解开仿函数(拉姆达和std ::功能):

#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