web-dev-qa-db-fra.com

En utilisant std :: bind avec la fonction membre, utilisez le pointeur d'objet ou non pour cet argument?

Lors de l'utilisation de std::bind pour lier une fonction membre, le premier argument est le pointeur objets this. Cependant, cela fonctionne en passant l'objet à la fois comme pointeur et non.

Voir par exemple le programme suivant:

#include <iostream>
#include <functional>

struct foo
{
    void bar(int v) { std::cout << "foo::bar - " << v << '\n'; }
};

int main()
{
    foo my_foo;

    auto f1 = std::bind(&foo::bar, my_foo, 1);
    auto f2 = std::bind(&foo::bar, &my_foo, 2);

    f1();
    f2();
}

Clang et GCC le compilent sans se plaindre, et le résultat fonctionne pour les deux liaisons:

 foo :: bar - 1 
 foo :: bar - 2 

J'ai essayé de faire le tour de la spécification (section 20.8.9) mais c'est l'un des endroits où c'est loin d'être clair pour moi.

Un seul doit-il être correct, ou les deux sont-ils corrects?

62

Les deux sont corrects. 20.8.9.1.2 transmet au 20.8.2 pour décrire les exigences et l'effet de votre appel à bind. 20.8.2 est:

20.8.2 Exigences [func.require]

1 Définissez INVOQUEZ (f, t1, t2, ..., tN) Comme suit:

- (t1.*f)(t2, ..., tN) Lorsque f est un pointeur vers une fonction membre d'une classe T et t1 Est un objet de type T ou un référence à un objet de type T ou une référence à un objet d'un type dérivé de T;

- ((*t1).*f)(t2, ..., tN) Lorsque f est un pointeur vers une fonction membre d'une classe T et t1 N'est pas l'un des types décrits dans l'élément précédent;

- t1.*f Lorsque N == 1 Et f est un pointeur vers les données de membre d'une classe T et t1 Est un objet de type T ou une référence à un objet de type T ou une référence à un objet d'un type dérivé de T;

- (*t1).*f Lorsque N == 1 Et f est un pointeur vers les données membres d'une classe T et t1 N'est pas un des types décrits dans l'élément précédent;

- f(t1, t2, ..., tN) dans tous les autres cas.

Les deux premières options permettent à la fois une référence et un pointeur.

La chose importante à noter ici est que la formulation ne pas vous limite aux simples pointeurs. Vous pouvez utiliser un std::shared_ptr Ou un autre pointeur intelligent pour maintenir votre instance en vie pendant qu'elle est liée et cela fonctionnerait toujours avec std::bind Car t1 Est déréférencé, quel qu'il soit ( étant donné, bien sûr, que c'est possible).

49
Daniel Frey

Pour ajouter à la bonne réponse (que les deux formulaires sont autorisés).

Je pense aux deux options de liaison en analogie avec la déclaration d'argument de fonction, qui peut être "passée par valeur" ou "passée par référence".

Dans le cas de f1 (aka passant my_foo "par valeur") le résultat ne "voit" aucune modification apportée à my_foo passé le point de liaison. Cela peut ne pas être souhaitable, surtout si my_foo évolue. La liaison "par valeur" a un "coût" supplémentaire de (plusieurs) appels à un constructeur de copie.

2
rytis