¿Es legal asignar de nuevo un puntero para que funcione?

33

Los punteros a funciones no son punteros de datos simples ya que no pueden almacenarse en un puntero nulo *. No obstante, parece que puedo almacenar la copia de un puntero de función en la memoria dinámica (en gcc y clang) como en el código a continuación. ¿Es legal ese código de acuerdo con el estándar C ++, o tal vez esta es una especie de extensión del compilador?

Además, el puntero resultante a puntero de función se comporta como un puntero de datos simple: puedo almacenarlo en void * y recuperarlo de void * mediante static_cast. ¿Es este comportamiento garantizado por la Norma?

int main()
{
  extern void fcn();
  void (*fcnPtr)() = &fcn;
  void (**ptrToFcnPtr)() = nullptr;

  //Make the copy of fcnPtr on the heap:
  ptrToFcnPtr = new decltype(fcnPtr)(fcnPtr);
  //Call the pointed-to function : 
  (**ptrToFcnPtr)();

  //Save the pointer in void* :
  void *ptr = ptrToFcnPtr;
  //retrieve the original ptr: 
  auto myPtr = static_cast< void(**)() > (ptr) ; 
  //free memory:
  delete ptrToFcnPtr ;

}
Adrian
fuente
2
Por favor, no use punteros de función sin procesar. Usar en su std::functionlugar.
Algún tipo programador
No necesitas el newpara lanzar void*. void* ptr = &fcnPtr;funciona igual de bien, ya que fcnPtres un objeto, no una función.
nogal
55
@Someprogrammerdude std::functiones un contenedor de tipo borrado para almacenar una llamada arbitraria, no es realmente un reemplazo para los punteros de función ...
Michael Kenzel
77
(@Someprogrammerdude) Por favor, no use ciegamente / recomiende std::function. Es excelente por su capacidad para almacenar funciones "polimórficas" (es decir, cualquier cosa con la firma correcta, incluso si contiene el estado como en el caso de algunas lambdas), pero eso también agrega una sobrecarga que puede no ser necesaria. Un puntero a una función es POD. A std::functionno lo es.
Mateo
2
@Matthew Para ser justos, Adrian pregunta sobre la asignación dinámica del puntero para que funcione y lo señala con un borrado de texto void*, por lo que en el contexto de esta pregunta std::functionparece ser exactamente lo que estaban buscando. Estoy de acuerdo en que el rechazo general de SPD de los punteros de función no es sólido.
Eerorika

Respuestas:

27

Si bien los punteros de función no son punteros de objeto, el "puntero a función de algún tipo" sigue siendo un tipo de objeto [basic.types] / 8 . Por lo tanto, los punteros de función son en sí mismos objetos, lo que apuntan no lo es.

Por lo tanto, puede crear un objeto de tipo puntero de función mediante una nueva expresión ...

Michael Kenzel
fuente
9

ya que ellos (punteros de función) no pueden almacenarse en un puntero nulo *.

En realidad, el almacenamiento de un puntero de función como a void*se admite condicionalmente. Esto significa que puede o no puede almacenarse dependiendo de la implementación del lenguaje. Si la implementación del lenguaje admite la carga dinámica, entonces void*probablemente se admite la conversión del puntero de función . GCC, Clang y MSVC son compatibles con esto:

reinterpret_cast<void*>(&function);

¿Es legal asignar de nuevo un puntero para que funcione?

Por supuesto. Todos los punteros, incluidos los punteros de función, son objetos y todos los objetos pueden asignarse dinámicamente.

Además, el puntero resultante al puntero de función se comporta como un puntero de datos sin formato.

El puntero de función es un objeto. El puntero a un puntero de función no solo "se comporta como", sino que es un puntero a un objeto.

Puedo almacenarlo en void * y recuperarlo de void * mediante static_cast. ¿Es este comportamiento garantizado por la Norma?

Se permite la conversión entre puntero a vacío y puntero a objeto, sí. Y la conversión de ida y vuelta garantiza el puntero original.

eerorika
fuente
Gracias. Pero luego, para convertir el puntero de función a void * (o al revés), necesito usar reinterpret_cast, ¿verdad?
Adrian
1
@ Adrian Sí. Agregué un ejemplo.
Eerorika
Si alguien quiere una excepción, probablemente: dos modelo de memoria media + superposiciones. Los punteros de función son más grandes que los punteros de datos en el modelo mediano, y las superposiciones se pueden usar para lograr complementos si se esfuerza lo suficiente.
Joshua