Comment std::bind fonctionne avec les fonctions de membre
Je travaille avec std::bind
mais je ne comprends toujours pas comment cela fonctionne quand on l'utilise avec des membre des fonctions de classe.
Si nous avons la fonction suivante:
double my_divide (double x, double y) {return x/y;}
Je comprends parfaitement les lignes suivantes de code:
auto fn_half = std::bind (my_divide,_1,2); //returns x/2
std::cout << fn_half(10) << '\n'; //5
Mais maintenant, avec le code suivant, où nous avons le lier à la fonction de membre j'ai quelques questions.
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
Foo foo;
auto f = std::bind(&Foo::print_sum, &foo, 95, _1);
f(5);
- Pourquoi le premier argument est une référence? J'aimerais avoir une explication théorique.
- Le deuxième argument est une référence à l'objet et c'est pour moi la partie la plus compliquée à comprendre. Je pense que c'est parce que
std::bind
a besoin d'un contexte, ai-je le droit? Est toujours comme ça? Astd::bind
une sorte de mise en œuvre, nécessitent une référence quand le premier argument est une fonction membre?
Un
En général, évitez d'utiliser
&
suivie par un identificateur ne signifie pas "de référence". Non, sauf si c'est la déclaration d'une variable, auquel cas le &
sera précédée par une typename. Lorsque &
est appliqué à un identifiant, vous obtenez un pointeur.En général, évitez d'utiliser
std::bind
. Il a quelques très profonde bizarreries qui font produire des résultats surprenants, même une fois que vous maîtrisez les principes de base. Je trouve qu'il ne vaut pas le coup.OriginalL'auteur Tarod | 2016-06-05
Vous devez vous connecter pour publier un commentaire.
Quand vous dites "le premier argument est une référence" vous avez sûrement voulu dire "le premier argument est un pointeur": le
&
opérateur prend l'adresse d'un objet, ce qui donne un pointeur.Avant de répondre à cette question, nous allons brièvement du recul et de regarder votre première utilisation de
std::bind()
lorsque vous utilisezvous fournir une fonction. Lorsqu'une fonction est votée il se désintègre en un pointeur. L'expression ci-dessus est équivalent à celui-ci, prenant explicitement l'adresse
Le premier argument de
std::bind()
est un objet de déterminer comment appeler une fonction. Dans le cas ci-dessus, il est un pointeur de fonction de typedouble(*)(double, double)
. Tout autre objet appelable, avec un appel de fonction de l'opérateur, trop.Depuis les fonctions membres sont tout à fait commun,
std::bind()
fournit un soutien pour faire face avec le pointeur de fonctions membres. Lorsque vous utilisez&print_sum
vous simplement obtenir un pointeur vers une fonction membre, c'est à dire, une entité de typevoid (Foo::*)(int, int)
. Alors que les noms de fonction implicitement à la carie de pointeurs de fonctions, c'est à dire, la&
peut être omis, ce n'est pas vrai pour les fonctions membres (ou membres de données, d'ailleurs): pour obtenir un pointeur vers une fonction membre, il est nécessaire d'utiliser le&
.Noter qu'un pointeur de membre est spécifique à un
class
mais il peut être utilisé avec n'importe quel objet de la classe. C'est, il est indépendant de tout objet particulier. C++ n'est pas un moyen direct d'obtenir une fonction de membre directement liée à un objet (je pense en C#, vous pouvez obtenir des fonctions directement liée à un objet à l'aide d'un objet par un nom de membre; toutefois, il est de 10 ans depuis la dernière fois que j'ai programmé un peu de C#).En interne,
std::bind()
détecte qu'un pointeur vers une fonction membre est passé et, plus vraisemblablement, il se transforme en un callable objets, par exemple, par l'utilisationstd::mem_fn()
avec son premier argument. Depuis un non-static
fonction membre a besoin d'un objet, le premier argument de la résolution de l'objet appelable est soit une référence ou un [smart] pointeur vers un objet de la classe appropriée.Utiliser un pointeur vers une fonction membre d'un objet est nécessaire. Lors de l'utilisation d'un pointeur de membre avec
std::bind()
le deuxième argument destd::bind()
en conséquence besoin de spécifier quand l'objet est à venir. Dans votre exemplel'résultant objet appelable utilise
&foo
, c'est à dire, un pointeur versfoo
(de typeFoo*
) que l'objet.std::bind()
est assez intelligent pour utiliser quoi que ce soit qui ressemble à un pointeur, rien convertibles à une référence du type approprié (commestd::reference_wrapper<Foo>
), ou [copier] d'un objet, de l'objet, lorsque le premier argument est un pointeur sur membre.Je soupçonne que vous n'avez jamais vu un pointeur sur membre - sinon, il serait tout à fait clair. Voici un exemple simple:
La fonction
apply()
obtient tout simplement deux pointeurs versFoo
objets, et un pointeur vers une fonction membre. Il appelle la fonction membre a souligné avec chacun des objets. Ce drôle de->*
opérateur demande un pointeur sur un membre à un pointeur vers un objet. Il y a aussi un.*
opérateur qui applique un pointeur sur un membre d'un objet (ou, comme ils se comportent comme des objets, une référence à un objet). Depuis un pointeur vers une fonction membre a besoin d'un objet, il est nécessaire d'utiliser cet opérateur qui demande un objet. En interne,std::bind()
organise le même.Quand
apply()
est appelée avec les deux pointeurs et&Foo::f
il se comporte exactement de la même manière que si le membref()
serait appelée sur les objets respectifs. De même, lors de l'appel deapply()
avec les deux pointeurs et&Foo::g
il se comporte exactement de la même manière que si le membreg()
serait appelée sur les objets respectifs (la sémantique du comportement est le même, mais le compilateur est susceptible d'avoir un temps beaucoup plus difficile inline fonctions et échoue généralement lorsque les pointeurs vers les membres sont impliqués).Incroyable réponse, Dietmar. Pour moi, les clés sont les suivantes: (1) Alors que les noms de fonction implicitement à la carie de pointeurs de fonctions, le même n'est pas vrai pour les fonctions de membre: pour obtenir un pointeur vers une fonction membre, il est nécessaire d'utiliser le &., (2) std::bind() détecte qu'un pointeur vers une fonction membre est passé et, plus vraisemblablement, il se transforme en un objet appelable, et (3) Pour utiliser un pointeur de fonction membre d'un objet est nécessaire. Lors de l'utilisation d'un pointeur de membre avec std::bind() le second argument de std::bind() en conséquence besoin de spécifier quand l'objet est à venir. Merci beaucoup!
OriginalL'auteur Dietmar Kühl
De std::bind docs:
bind( F&& f, Args&&... args );
où f est uneCallable
, dans votre cas, c'est un pointeur vers une fonction membre. Ce type de pointeurs a quelques syntaxe particulière par rapport à des pointeurs de fonctions usuelles:std::bind
(etstd::invoke
en général) couvre tous ces cas de manière uniforme. Sif
est un pointeur vers membre deFoo
, puis la premièreArg
à condition de lier devrait être une instance deFoo
(bind(&Foo::print_sum, foo, ...)
fonctionne aussi, maisfoo
est copié) ou d'un pointeur àFoo
, comme dans l'exemple que vous aviez.Voici quelques plus de lecture sur des pointeurs vers les membres de l', et Un et Deux donne plein d'informations sur ce qui se lient attend et comment appeler la fonction stockée.
Vous pouvez également utiliser les lambdas au lieu
std::bind
, qui pourrait être plus clair:&Foo::print_sum
est pas un callable! Un pointeur de membre, même un pointeur vers une fonction membre , ne pas ont un appel de fonction de l'opérateur!std::mem_fn(&Foo::print_sum)
serait un appelable. Cependant,std::bind()
ne comprends d'être appelé avec le pointeur de membres en tant que premier argument."Callable" signifie un peu autre chose en c++17: en.cppreference.com/w/cpp/types/is_callable. Et toute la famille de
std::mem_fn
,std::bind
etstd::invoke
, il suffit de fournir une interface agréable sur la base d'une Appelable types.Mais c'est juste une question de terminologie.
Merci @DmitryPanteleev! Je lisais aussi une très bonne réponse de ici liés avec des pointeurs de fonction et des pointeurs vers des fonctions de membre du.
OriginalL'auteur Dmitry Panteleev