Czy legalne jest przydzielanie nowego wskaźnika do działania?

33

Wskaźniki do funkcji nie są zwykłymi wskaźnikami danych, ponieważ nie mogą być przechowywane w pustym wskaźniku *. Niemniej jednak wydaje się, że mogę przechowywać kopię wskaźnika funkcji w pamięci dynamicznej (w gcc i clang), jak w poniższym kodzie. Czy taki kod jest zgodny ze standardem C ++, a może jest to jakieś rozszerzenie kompilatora?

Ponadto wynikowy wskaźnik do wskaźnika funkcji zachowuje się jak zwykły wskaźnik danych: mogę przechowywać go w void * i odzyskać z void * przez static_cast. Czy takie zachowanie jest gwarantowane przez standard?

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
źródło
2
Proszę nie używać surowych wskaźników funciton. Użyj std::functionzamiast tego.
Jakiś programista koleś
Nie potrzebujesz newrzucić na void*. void* ptr = &fcnPtr;działa równie dobrze, ponieważ fcnPtrjest przedmiotem, a nie funkcją.
orzech
5
@Someprogrammerdude std::functionto skasowany typ kontenera do przechowywania dowolnych wywołań, w rzeczywistości nie zastępujący wskaźników funkcji ...
Michael Kenzel
7
(@Someprogrammerdude) Nie używaj / polecaj na ślepo std::function. Jest świetny ze względu na możliwość przechowywania funkcji „polimorficznych” (tj. Cokolwiek z odpowiednią sygnaturą, nawet jeśli zawiera stan jak w przypadku niektórych lambdów), ale to także powoduje dodatkowe obciążenie, które może nie być potrzebne. Wskaźnik do funkcji to POD. A std::functionnie jest.
Matthew
2
@Matthew Aby być uczciwym, Adrian prosi o dynamiczne przydzielanie wskaźnika do funkcji i wskazywanie go za pomocą funkcji wymazywania void*tekstu, więc w kontekście tego pytania std::functionwydaje się dokładnie tym, czego szukali. Zgadzam się, że ogólne odrzucenie wskaźników funkcji przez SPD jest nieuzasadnione.
eerorika

Odpowiedzi:

27

Podczas gdy wskaźniki funkcji nie są wskaźnikami obiektowymi, „wskaźnik do funkcji jakiegoś typu” jest nadal typem obiektu [basic.types] / 8 . Tak więc wskaźniki funkcji same w sobie są obiektami, tylko tym, na co wskazują, nie jest.

Tak więc z pewnością możesz utworzyć obiekt typu wskaźnika funkcji za pomocą nowego wyrażenia…

Michael Kenzel
źródło
9

ponieważ (wskaźniki funkcji) nie mogą być przechowywane w pustym wskaźniku *.

W rzeczywistości przechowywanie wskaźnika funkcji jako void*warunku jest obsługiwane warunkowo. Oznacza to, że albo może, albo nie może być przechowywany w zależności od implementacji języka. Jeśli implementacja języka obsługuje ładowanie dynamiczne, void*prawdopodobnie obsługiwany jest wskaźnik konwersji funkcji . GCC, Clang i MSVC obsługują to:

reinterpret_cast<void*>(&function);

Czy legalne jest przydzielanie nowego wskaźnika do działania?

Pewnie. Wszystkie wskaźniki, w tym wskaźniki funkcji, są obiektami i wszystkie obiekty mogą być przydzielane dynamicznie.

Ponadto wynikowy wskaźnik do wskaźnika funkcji zachowuje się jak zwykły wskaźnik danych

Wskaźnik funkcji jest obiektem. Wskaźnik do wskaźnika funkcji nie tylko „zachowuje się jak”, ale jest wskaźnikiem do obiektu.

Mogę przechowywać go w void * i odzyskać z void * przez static_cast. Czy takie zachowanie jest gwarantowane przez standard?

Konwersja między wskaźnikiem na pustkę i wskaźnikiem na obiekt jest dozwolona, ​​tak. A konwersja w obie strony gwarantuje uzyskanie oryginalnego wskaźnika.

eerorika
źródło
Dzięki. Ale następnie, aby przekonwertować wskaźnik funkcji na void * (lub odwrotnie), muszę użyć reinterpret_cast, prawda?
Adrian
1
@Adrian Tak. Dodałem przykład.
eerorika
Jeśli ktoś chce wyjątku od prawdopodobnie: modelu pamięci nośnika dos + nakładek. Wskaźniki funkcji są większe niż wskaźniki danych w modelu średnim, a nakładki można wykorzystać do uzyskania wtyczek, jeśli spróbujesz wystarczająco mocno.
Joshua