Первый вариант:
template<typename Func> void MyFunc (const Func & otherFunc);
Гарантирует, что otherFunc будет заинлайнена в MyFunc, то есть такой вариант предпочтителен там, где требуется максимальная производительность. Но шаблонные функции не могут быть виртуальными. То есть такой вариант очень хорошо подходит для функций-утилит, и плохо подходит для методов публичного API и на границах модулей, так как там как раз инлайн не нужен, а нужны виртуальность и сокрытие реализации.
Важное замечание! Приведённый код не совсем корректен, всегда в таких случаях используйте передачу по универсальной ссылке:
template<typename Func> void MyFunc (Func&& otherFunc);
Такой вариант покрывает и константные функторы, и те, которым необходима мутабельность для работы. Если вы планируете сохранить функтор, то это ещё и позволит воспользоваться perfect forwarding -- по сути, единственный способ, корректно работающий с любым переданным функтором:
field = std::forward<Func>(otherFunc);
Второй вариант:
void MyFunc (const auto & otherFunc);
Можно использовать, если вам не нужен тип функтора. По сути он эквивалентен предыдущему.
Третий вариант:
void MyFunc (const std::function<void()> & otherFunc);
Возьмите отрицание моих предыдущих аргументов и примените сюда. Можно использовать с виртуальными функциями, больше подходит для API, но теряет в производительности. А именно, вызов otherFunc эквивалентен вызову виртуальной функции.
Четвёртый вариант:
void MyFunc (const void (&otherFunc)());
Работает только с простыми, глобально определёнными функциями. Вместо этого практически всегда стоит использовать std::function.