Ci sono alcuni pezzi che consentono a tutte queste combinazioni di operatori di lavorare allo stesso modo.
Il motivo fondamentale per cui tutti questi lavori è che una funzione (come foo
) è implicitamente convertibile in un puntatore alla funzione. Ecco perché void (*p1_foo)() = foo;
funziona: foo
viene implicitamente convertito in un puntatore a se stesso e quel puntatore è assegnato a p1_foo
.
L'unario &
, quando applicato a una funzione, fornisce un puntatore alla funzione, proprio come produce l'indirizzo di un oggetto quando viene applicato a un oggetto. Per i puntatori a funzioni ordinarie, è sempre ridondante a causa della conversione implicita da funzione a funzione. In ogni caso, questo è il motivo per cui void (*p3_foo)() = &foo;
funziona.
L'unario *
, quando applicato a un puntatore a funzione, produce la funzione a punta, proprio come produce l'oggetto a punta quando viene applicato a un normale puntatore a un oggetto.
Queste regole possono essere combinate. Considera il tuo penultimo esempio **foo
:
- Innanzitutto,
foo
viene implicitamente convertito in un puntatore a se stesso e il primo *
viene applicato a quel puntatore di funzione, restituendo foo
nuovamente la funzione .
- Quindi, il risultato viene nuovamente convertito implicitamente in un puntatore a se stesso e
*
viene applicato il secondo , restituendo nuovamente la funzione foo
.
- Viene quindi nuovamente implicitamente convertito in un puntatore a funzione e assegnato alla variabile.
Puoi aggiungere quanti *
s vuoi, il risultato è sempre lo stesso. Più *
s, più è bello.
Possiamo anche considerare il tuo quinto esempio &*foo
:
- Innanzitutto,
foo
viene implicitamente convertito in un puntatore a se stesso; *
viene applicato l'unario , cedendo di foo
nuovo.
- Quindi,
&
viene applicato a foo
, restituendo un puntatore a foo
, che viene assegnato alla variabile.
Tuttavia, &
può essere applicato solo a una funzione, non a una funzione che è stata convertita in un puntatore a funzione (a meno che, ovviamente, il puntatore a funzione sia una variabile, nel qual caso il risultato è un puntatore a un puntatore- to-a-function; ad esempio, è possibile aggiungere all'elenco void (**pp_foo)() = &p7_foo;
).
Ecco perché &&foo
non funziona: &foo
non è una funzione; è un puntatore a funzione che è un valore. Tuttavia, &*&*&*&*&*&*foo
funzionerebbe, come farebbe &******&foo
, perché in entrambe queste espressioni &
viene sempre applicato a una funzione e non a un puntatore a funzione rvalore.
Si noti inoltre che non è necessario utilizzare unario *
per effettuare la chiamata tramite il puntatore a funzione; entrambi (*p1_foo)();
e (p1_foo)();
hanno lo stesso risultato, sempre a causa della conversione da funzione a funzione-puntatore.
&foo
prende l'indirizzo difoo
, il che si traduce in un puntatore a funzione che punta afoo
, come ci si aspetterebbe.