Comment imprimer des pointeurs de fonctions avec cout?

Je veux imprimer un pointeur de fonction en utilisant cout, et j’ai trouvé que cela ne fonctionnait pas. Mais cela a fonctionné après avoir converti le pointeur de fonction en (void *), de même que printf avec% p, tel que

#include  using namespace std; int foo() {return 0;} int main() { int (*pf)(); pf = foo; cout << "cout << pf is " << pf << endl; cout << "cout << (void *)pf is " << (void *)pf << endl; printf("printf(\"%%p\", pf) is %p\n", pf); return 0; } 

Je l’ai compilé avec g ++ et j’ai obtenu des résultats comme ceci:

cout << pf est 1
cout << (void *) pf est 0x100000b0c
printf (“% p”, pf) est 0x100000b0c

Alors, que fait cout avec le type int (*) ()? On m’a dit que le pointeur de fonction est traité comme bool, est-ce vrai? Et que fait cout avec le type (void *)?

Merci d’avance.

EDIT: De toute façon, nous pouvons observer le contenu d’un pointeur de fonction en le convertissant en (void *) et l’imprimer en utilisant cout. Mais cela ne fonctionne pas pour les pointeurs de fonction membres et le compilateur se plaint de la conversion illégale. Je sais que les pointeurs des fonctions membres sont plutôt une structure compliquée que des simples pointeurs, mais comment pouvons-nous observer le contenu des pointeurs d’une fonction membre?

Il y a en fait une surcharge de l’opérateur << qui ressemble à quelque chose comme:

 ostream & operator <<( ostream &, const void * ); 

qui fait ce à quoi vous vous attendez - des sorties en hexadécimal. Il ne peut y avoir aucune surcharge de bibliothèque standard pour les pointeurs de fonction, car ils sont un nombre infini de types. Donc, le pointeur est converti en un autre type, qui dans ce cas semble être un booléen - je ne peux pas oublier les règles pour cela.

Edit: Le standard C ++ spécifie:

4.12 Conversions booléennes

1 Une valeur d'arithmétique, d'énumération, de pointeur ou de pointeur sur le type de membre peut être convertie en une valeur de type bool.

C'est la seule conversion spécifiée pour les pointeurs de fonction.

En ce qui concerne votre modification, vous pouvez imprimer le contenu de tout en y accédant via un pointeur de caractère unsigned char . Un exemple de pointeur vers les fonctions membres:

 #include  #include  struct foo { virtual void bar(){} }; struct foo2 { }; struct foo3 : foo2, foo { virtual void bar(){} }; int main() { void (foo3::*p)() = &foo::bar; unsigned char const * first = reinterpret_cast(&p); unsigned char const * last = reinterpret_cast(&p + 1); for (; first != last; ++first) { std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)*first << ' '; } std::cout << std::endl; } 

Vous pouvez penser à un pointeur de fonction comme étant l’adresse de la première instruction du code machine de cette fonction. Tout pointeur peut être traité comme un bool : 0 est faux et tout le rest est vrai. Comme vous l’avez observé, lorsqu’elle est void * et donnée en argument à l’opérateur d’insertion de stream ( << ), l’adresse est imprimée. (Vu de manière ssortingcte, la conversion d'un pointeur vers une fonction pour void * n'est pas définie.)

Sans le casting, l'histoire est un peu complexe. Pour les fonctions surchargées correspondantes ("résolution de surcharge"), un compilateur C ++ regroupe un ensemble de fonctions candidates et sélectionne, parmi ces candidats, la "meilleure viable", en utilisant si nécessaire des conversions implicites. La ride est que les règles de correspondance forment un ordre partiel, de sorte que plusieurs correspondances les plus viables provoquent une erreur d'ambiguïté.

Par ordre de préférence, les conversions standard (et bien sûr aussi les conversions définies par l'utilisateur et par points de suspension, non détaillées) sont

  • correspondance exacte ( c. -à- d . aucune conversion nécessaire)
  • promotion ( par exemple , int pour float )
  • autres conversions

La dernière catégorie inclut les conversions booléennes et tout type de pointeur peut être converti en bool : 0 (ou NULL ) est false et tout le rest est true . Ce dernier apparaît comme 1 lorsqu'il est transmis à l'opérateur d'insertion de stream.

Pour obtenir 0 place, changez votre initialisation en

 pf = 0; 

Rappelez-vous que l'initialisation d'un pointeur avec une expression constante à valeur zéro génère le pointeur null.

Lancer des pointeurs vers (void*) pour les imprimer à cout est la bonne chose à faire en C ++ si vous voulez voir leurs valeurs.

En ce qui concerne votre question spécifique,

Comment pouvons-nous observer le contenu des pointeurs d’une fonction membre?

La réponse est que, en dehors de les convertir en bool pour exprimer qu’il pointe vers quelque chose ou non, vous ne pouvez pas «observer» les pointeurs de la fonction membre. Au moins pas de manière conforme. La raison en est que la norme interdit explicitement cela:

4.12 note de bas de page 57:

57) La règle de conversion des pointeurs en membres (du pointeur au membre de base en pointeur en membre de dérivé) apparaît inversée par rapport à la règle des pointeurs vers les objects (du pointeur vers le dérivé vers le pointeur vers la base) (4.10, clause 10) . Cette inversion est nécessaire pour assurer la sécurité du type. Notez qu’un pointeur sur un membre n’est pas un pointeur sur un object ou un pointeur sur la fonction et que les règles pour les conversions de tels pointeurs ne s’appliquent pas aux pointeurs vers les membres. En particulier, un pointeur sur un membre ne peut pas être converti en un vide *.

Par exemple, voici un exemple de code:

 #include  #include  #include  #include  #include  using namespace std; class Gizmo { public: void DoTheThing() { return; }; private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; return 0; } 

Je note que mon débogueur (MSVC9) est capable de me dire l'adresse physique réelle de la fonction membre lors de l'exécution, donc je sais qu'il doit y avoir un moyen d'obtenir réellement cette adresse. Mais je suis sûr que ce n'est pas conforme, non portable et implique probablement du code machine. Si je devais suivre cette voie, je commencerais par prendre l'adresse du pointeur de la fonction (par exemple, &fn ), en annulant celle-ci * et en partant de là. Cela nécessiterait également de connaître la taille des pointeurs (différents sur différentes plates-formes).

Mais je vous le demande, tant que vous pouvez convertir le pointeur membre-fonction en booléen et évaluer l'existance du pointeur, pourquoi dans le code réel auriez-vous besoin de l'adresse?

Vraisemblablement, la réponse à la dernière question est "donc je peux déterminer si un pointeur de fonction pointe vers la même fonction qu'un autre." C'est suffisant. Vous pouvez comparer les pointeurs de fonction pour l'égalité:

 #include  #include  #include  #include  #include  using namespace std; class Gizmo { public: void DoTheThing() { return; }; **void DoTheOtherThing() { return; };** private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; **void(Gizmo::*fnOther)(void) = &Gizmo::DoTheOtherThing; bool same = fnOther == fn; bool sameIsSame = fn == fn;** return 0; } 

En C ++ 11, il est possible de modifier ce comportement en définissant une surcharge de modèle variadic d’ operator<< (que ce soit recommandé ou non est un autre sujet):

 #include namespace function_display{ template std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ){ // star * is optional return os << "funptr " << (void*)p; } } // example code: void fun_void_void(){}; void fun_void_double(double d){}; double fun_double_double(double d){return d;} int main(){ using namespace function_display; // ampersands & are optional std::cout << "1. " << &fun_void_void << std::endl; // prints "1. funptr 0x40cb58" std::cout << "2. " << &fun_void_double << std::endl; // prints "2. funptr 0x40cb5e" std::cout << "3. " << &fun_double_double << std::endl; // prints "3. funptr 0x40cb69" }