Если я правильно понимаю, как все это работает, проблема в том, что в первых трех случаях вы передаете указатель на функцию, которая по самой своей природе должна указывать на четко определенную перегрузку функции, это не относится к строке 20, так как там вы указываете целый класс функций (каждая из которых, кстати, должна быть явно сгенерирована компилятором при необходимости). Правила вывода типов здесь не применяются, так как, если вы передадите указатель на функцию, все игры будут закрыты до вычисляется тело count_if
(тип указателя на функцию должен быть определено до создания шаблона count_if
).
Вместо этого в строке 21 вы указываете, какую именно функцию вы передаете, на самом деле компилятор создает экземпляр шаблона для типа int и передает указатель на эту функцию.
В строке 22 происходит совершенно другое. Вы передаете четко определенный тип объекта count_if
. В теле count_if
затем, когда вызывается operator()
, у компилятора есть вся информация, чтобы вывести ему аргумент шаблона, так как он фактически вызывает его и имеет реальные параметры, из которых он может выполнять вывод типа.