Cómo imprimir punteros de función con cout?

Quiero imprimir un puntero de función usando cout, y encontré que no funcionó. Pero funcionó después de convertir el puntero de función a (void *), también lo hace printf con% p, como

#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; } 

Lo compilé con g ++ y obtuve resultados como este:

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

Entonces, ¿qué hace cout con type int (*) ()? Me dijeron que el puntero a la función se trata como bool, ¿es cierto? ¿Y qué hace cout con type (void *)?

Gracias por adelantado.

EDITAR: De todos modos, podemos observar el contenido de un puntero de función convirtiéndolo en (vacío *) e imprimirlo usando cout. Pero no funciona para los punteros de función de miembro y el comstackdor se queja de la conversión ilegal. Sé que los punteros de funciones de los miembros son una estructura bastante complicada que no son punteros simples, pero ¿cómo podemos observar el contenido de los punteros de función de un miembro?

En realidad, hay una sobrecarga del operador << que se ve algo así como:

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

que hace lo que esperas: salidas en hex. No puede haber una sobrecarga de biblioteca estándar para punteros de función, porque hay un número infinito de tipos de ellos. Entonces el puntero se convierte a otro tipo, que en este caso parece ser un bool, no puedo recordar las reglas para esto.

Editar: El estándar de C ++ especifica:

4.12 conversiones booleanas

1 Un valor de aritmética, enumeración, puntero o puntero a tipo de miembro se puede convertir a un valor r de tipo bool.

Esta es la única conversión especificada para los punteros de función.

En cuanto a su edición, puede imprimir los contenidos de cualquier cosa accediendo a ella a través del puntero de unsigned char . Un ejemplo para punteros a funciones miembro:

 #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; } 

Puede pensar en un puntero de función como la dirección de la primera instrucción en el código máquina de esa función. Cualquier puntero puede tratarse como un bool : 0 es falso y todo lo demás es verdadero. Como observó, cuando se convierte en void * y se da como argumento al operador de inserción de flujo ( << ), la dirección se imprime. (Visto estrictamente, lanzar un puntero a función para void * no está definido).

Sin el elenco, la historia es un poco compleja. Para funciones coincidentes sobrecargadas ("resolución de sobrecarga"), un comstackdor de C ++ reúne un conjunto de funciones candidatas y de estos candidatos selecciona el "mejor viable", utilizando conversiones implícitas si es necesario. Las arrugas son las reglas de coincidencia que forman un orden parcial, por lo que las coincidencias más confiables causan un error de ambigüedad.

En orden de preferencia, las conversiones estándar (y, por supuesto, también las conversiones definidas por el usuario y elipsis, no detalladas) son

  • coincidencia exacta ( es decir , no es necesaria conversión)
  • promoción ( por ejemplo , int para float )
  • otras conversiones

La última categoría incluye conversiones booleanas, y cualquier tipo de puntero puede convertirse a bool : 0 (o NULL ) es false y todo lo demás es true . Este último aparece como 1 cuando se pasa al operador de inserción de flujo.

Para obtener 0 en su lugar, cambie su inicialización a

 pf = 0; 

Recuerde que inicializar un puntero con una expresión constante de valor cero produce el puntero nulo.

Casting punteros a (void*) para imprimirlos para cout es lo correcto (TM) para hacer en C ++ si desea ver sus valores.

En cuanto a su pregunta específica,

¿cómo podemos observar el contenido de los punteros de función de un miembro?

La respuesta es que, además de convertirlos a bool para express que apunta a algo o no, no se pueden punteros de función miembro ‘observador’. Al menos no de una manera obediente. La razón es porque el estándar explícitamente no permite esto:

4.12 nota al pie 57:

57) La regla para la conversión de punteros a miembros (de puntero a miembro de base a puntero a miembro de derivado) aparece invertida en comparación con la regla para punteros a objetos (de puntero a derivado a puntero a base) (4.10, cláusula 10) . Esta inversión es necesaria para garantizar la seguridad del tipo. Tenga en cuenta que un puntero al miembro no es un puntero al objeto o un puntero a la función y las reglas para las conversiones de dichos punteros no se aplican a los punteros a los miembros. En particular, un puntero a miembro no se puede convertir a un vacío *.

Por ejemplo, aquí está el código de muestra:

 #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; } 

Observo que mi depurador (MSVC9) puede decirme la dirección física real de la función miembro en tiempo de ejecución, así que sé que debe haber alguna forma de obtener esa dirección. Pero estoy seguro de que no es conforme, no es portátil y probablemente involucra código de máquina. Si tuviera que ir por ese camino, comenzaría por tomar la dirección del puntero a la función (por ejemplo, &fn ), fundirla en el vacío *, e ir desde allí. Esto también requeriría que conozcas el tamaño de los punteros (diferentes en diferentes plataformas).

Pero me gustaría preguntar, siempre y cuando pueda convertir el puntero de función de miembro a bool y evaluar la existencia del puntero, ¿por qué en código real necesitaría la dirección?

Presumiblemente, la respuesta a la última pregunta es "para que pueda determinar si un puntero de función apunta a la misma función que otro". Lo suficientemente justo. Puede comparar indicadores de función para la igualdad:

 #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 uno podría modificar este comportamiento definiendo una sobrecarga de plantilla variable del operator<< (si eso es recomendable o no es otro tema):

 #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" }