La versione basata su modelli consente al compilatore di inline la chiamata, perché l'indirizzo della funzione è noto al momento della compilazione. Ovviamente, lo svantaggio è che l'indirizzo della funzione è noto a in fase di compilazione (poiché lo si utilizza come argomento del modello) e talvolta ciò potrebbe non essere possibile.
Questo ci porta al secondo caso, dove il puntatore di funzione può essere determinato solo in fase di esecuzione, rendendo quindi impossibile per il compilatore eseguire l'inlining, ma dando la flessibilità di determinare in fase di esecuzione la funzione di essere chiamato:
bool runtimeBooleanExpr = /* ... */;
doParam(0, runtimeBooleanExpr ? add1 : add2);
si noti, tuttavia, che ci sia una terza via:
template<typename F>
void doParam(int i, F f){
std::cout << "Do Param: " << f(i) << "\n";
}
che vi dà più flessibilità e ha ancora il vantaggio di sapere al momento della compilazione quale funzione sta per essere chiamato:
doParam(0, add1);
doParam(0, add2);
E permette anche passando qualsiasi oggetto richiamabile invece di un puntatore a funzione:
doParam(0, my_functor());
int fortyTwo = 42;
doParam(0, [=] (int i) { return i + fortyTwo; /* or whatever... */ }
Per completezza, v'è anche un quarto modo , utilizzando std::function
:
void doParam(int x, std::function<int(int)> f);
Che ha lo stesso livello di generalità (nel senso che è possibile passare qualsiasi oggetto callable), ma consente anche per determinare l'oggetto callable in fase di esecuzione - molto probabilmente con una penalizzazione delle prestazioni, dal momento che (ancora una volta) l'inlining diventa impossibile per il compilatore.
Per un'ulteriore discussione sulle ultime due opzioni, vedere anche this Q&A on StackOverflow.
Provate 'doParam (0, alcune_condizioni? Add1: add2)' per vedere la differenza. –