2017-09-29 3 views
1

J'essayais de jouer avec std :: function et std :: bind et j'ai posé un problème. Je voudrais construire une structure générale qui me permet de lier une fonction std :: à une fonction membre sans connaître a priori les arguments de la fonction membre. J'ai écrit cette choseViolation d'accès sur std :: function assignée à un lambda

template<typename Class, typename Return, typename ...Args> 
struct Caller 
{ 
private: 
    std::function<Return(Args ...)> callerFunction; 

    Caller(const Caller&) = delete; 
    Caller(Caller&&) = delete; 
    Caller& operator=(const Caller&) = delete; 

public: 
    ~Caller() = default; 
    Caller() = default; 

    Caller(Class& instance, Return(Class::*function)(Args...)) 
    { 
     callerFunction = [&](Args... args) { return (instance.*function)(args...); }; 
    } 

    Return operator() (Args ... args) 
    { 
     return callerFunction(args...); 
    } 
}; 

Pour votre information, je sais que les arguments de la fonction sont passés par valeur (je rencontrais un problème en utilisant des références universelles avec modèle variadique, je travaillerai plus tard).

Le problème ici est que lorsque j'exécute la fonction avec operator(), j'obtiens une erreur de violation d'accès. J'ai essayé d'affiner le problème et de créer une structure sans les arguments variadiques (permettant à la fonction membre d'avoir juste un argument int) et j'ai vu que l'assignation du lambda à la fonction std :: me donnait la même erreur, mais J'ai utilisé std :: bind avec un espace réservé tout allait bien.

Le terrain d'essai est ce

class A 
{ 
public: 
    bool foo(int a) 
    { 
     std::cout << a << std::endl; 
     return true; 
    } 
}; 

int main() 
{ 
    A a; 
    a.foo(9); 

    Caller<A, bool, int> caller(a, &A::foo); 
    caller(10); 

    std::cin.ignore(); 
} 

En utilisant le lambda, dois-je enregistrer l'instance de la classe pour appeler correctement la fonction de membre?

+1

Je suis sûr que vous avez UB. Dans le constructeur pour 'Caller', vous passez le pointeur de la fonction membre par valeur, (donc une copie du pointeur sera créée qui n'existe que dans le constructeur), mais ensuite capturez-le dans le lambda par référence. Ainsi, lorsque vous appelez 'caller (10)', le pointeur est hors de portée. 'instance' est bien dans cet exemple puisque vous passez cela au constructeur par référence, et' a' est toujours dans la portée quand vous appelez 'caller (10)'. (Bien que vous devriez supposer que cela changera dans le futur et concevra autour de cela.) – 0x5453

+1

Il est également intéressant de noter qu'il existe déjà un 'std :: mem_fn' qui devrait fonctionner correctement avec lambdas ou' std :: bind'. – 0x5453

+0

Ok compris. Ce que je ne comprends pas, c'est comment je peux résoudre ce problème. A propos de l'instance: au démarrage du programme, certaines classes seront instanciées et ces variables seront disponibles jusqu'à la fin du programme. – Astinog

Répondre

1

Comme état dans le commentaire, vous avez pointeur ballants de function, vous pouvez utiliser à la place:

Caller(Class& instance, Return(Class::*function)(Args...)) 
{ 
    callerFunction = [&instance, function](Args... args) { 
     return (instance.*function)(std::forward<Args>(args)...); 
    }; 
} 

Note: instance devrait également Caller survit.

0

N'utilisez pas [&] lorsque l'objet ou les copies de celui-ci survivent à la portée actuelle.

Vous capturez des références aux variables locales et vous les stockez au-delà de la portée actuelle.