2017-01-04 1 views
2

J'essaie de convertir le pointeur en fonction membre (en utilisant std::bind ou lambdas) en std::function. Ma tentative (suite à une réponse de this answer on SO) ressemble à ceci:Comment convertir std :: bind (ou lambda) en std :: function dans le contexte déduit?

#include <functional> 

template<typename T> 
struct AsFunction : 
    AsFunction<decltype(&T::operator())> 
{}; 

template<class ReturnType, class... Args> 
struct AsFunction<ReturnType(Args...)> { 
    using type = std::function<ReturnType(Args...)>; 
}; 

template<class ReturnType, class... Args> 
struct AsFunction<ReturnType(*)(Args...)> { 
    using type = std::function<ReturnType(Args...)>; 
}; 

template<class Class, class ReturnType, class... Args> 
struct AsFunction<ReturnType(Class::*)(Args...) const> { 
    using type = std::function<ReturnType(Args...)>; 
}; 

template<class F> 
auto toFunction(F f) -> typename AsFunction<F>::type { 
    return {f}; 
} 

struct MyStruct { 
    int x,y; 
    void f(int){}; 
}; 

int main(){ 
    MyStruct m; 

    { 
    // this works 
    auto f = std::bind(&MyStruct::f, &m, std::placeholders::_1); 
    f(2); 
    } 

    { 
    // this doesn't 
    auto f = toFunction(std::bind(&MyStruct::f, &m, std::placeholders::_1)); 
    f(2);                                                                  
    } 

    { 
    // .. neither does this 
    auto f = toFunction([m](int x) mutable { m.f(x); }); 
    f(2); 
    } 
} 

mais je reçois les messages d'erreur suivants du compilateur:

// first not working 
main.cpp:24:6: note: substitution of deduced template arguments resulted in errors seen above 
main.cpp: In instantiation of ‘struct AsFunction<std::_Bind<std::_Mem_fn<void (MyStruct::*)(int)>(MyStruct*, std::_Placeholder<1>)> >’: 
main.cpp:24:6: required by substitution of ‘template<class F> typename AsFunction<F>::type toFunction(F) [with F = std::_Bind<std::_Mem_fn<void (MyStruct::*)(int)>(MyStruct*, std::_Placeholder<1>)>]’ 
main.cpp:44:75: required from here 
main.cpp:4:8: error: decltype cannot resolve address of overloaded function 
struct AsFunction : 
     ^~~~~~~~~~ 
main.cpp: In function ‘int main()’: 
main.cpp:44:75: error: no matching function for call to ‘toFunction(std::_Bind_helper<false, void (MyStruct::*)(int), MyStruct*, const std::_Placeholder<1>&>::type)’ 
    auto f = toFunction(std::bind(&MyStruct::f, &m, std::placeholders::_1)); 
                     ^
main.cpp:24:6: note: candidate: template<class F> typename AsFunction<F>::type toFunction(F) 
auto toFunction(F f) -> typename AsFunction<F>::type { 
     ^~~~~~~~~~ 
main.cpp:24:6: note: substitution of deduced template arguments resulted in errors seen above 

// second non working braces with lambda 
main.cpp: In instantiation of ‘struct AsFunction<void (main()::<lambda(int)>::*)(int)>’: 
main.cpp:4:8: required from ‘struct AsFunction<main()::<lambda(int)> >’ 
main.cpp:24:6: required by substitution of ‘template<class F> typename AsFunction<F>::type toFunction(F) [with F = main()::<lambda(int)>]’ 
main.cpp:50:55: required from here 
main.cpp:5:23: error: ‘operator()’ is not a member of ‘void (main()::<lambda(int)>::*)(int)’ 
    AsFunction<decltype(&T::operator())> 
         ^~ 
main.cpp:50:55: error: no matching function for call to ‘toFunction(main()::<lambda(int)>)’ 
    auto f = toFunction([m](int x) mutable { m.f(x); }); 
                ^
main.cpp:24:6: note: candidate: template<class F> typename AsFunction<F>::type toFunction(F) 
auto toFunction(F f) -> typename AsFunction<F>::type { 
     ^~~~~~~~~~ 
main.cpp:24:6: note: substitution of deduced template arguments resulted in errors seen above 
+0

@Holt Je pense que l'OP veut déduire le type de résultat et type d'arguments. Ai-je raison? –

+0

@ W.F. exactement (je veux les manipuler plus tard) – Patryk

+0

Bon, et avez-vous vraiment besoin d'utiliser un * lambda * ou un 'std :: bind'? 'ToFunction (& MyStruct :: f, & m)' ne serait-il pas correct? Puisque "lambda type" et le retour de "std :: bind" sont définis par l'implémentation, il est toujours difficile de les utiliser pour déduire des arguments de template ... – Holt

Répondre

2

Il est difficile de déduire des arguments à partir d'un type de retour lambda ou std::bind, vous pouvez donc vouloir retarder la liaison à l'intérieur de toFunction, par ex. avec:

template<typename C, typename Ret, typename... Args, typename... BArgs> 
auto toFunction(Ret (C::*f)(Args...), BArgs&&... bargs) { 
    return std::function<Ret(Args...)>(std::bind(f, std::forward<BArgs>(bargs)...)); 
} 

De cette façon, vous pouvez récupérer un std::function en utilisant:

auto f = toFunction(&MyStruct::f, &m, std::placeholders::_1); 
f(2); 
0

Je pense que vous overkilling cela un peu. Si vous voulez vraiment un type std::function explicite pour votre f, vous pouvez simplement spécifier directement avec une supercherie:

int main(){ 
    MyStruct m; 

    { 
    std::function<decltype(std::declval<MyStruct>().f(int()))(int)> f(std::bind(&MyStruct::f, &m, std::placeholders::_1)); 
    f(2);                                                                  
    } 
} 

Ceci étant dit, la chose la plus simple est de loin d'utiliser simplement auto f sans spécifier un type. Je suppose que vous avez déjà pensé à cela et cela ne sera pas le cas dans votre cas, mais je le dis au cas où.

+0

Je pense que de cette façon vous auriez besoin de connaître les types d'arguments ... –

+1

Ah je vois. Eh bien, ce qui précède peut tout aussi bien être déplacé vers votre appel 'toFunction' avec des types de templates pour vous donner les types d'arguments. Permettez-moi de modifier la réponse pour démontrer – Smeeheey