Указание значения по умолчанию для аргумента функции шаблона

Не могли бы вы объяснить, почему следующий код не компилируется? Очевидным обходным решением является добавление 1-аргументной перегрузки Apply, есть ли более простая?

template <typename T>
T Identity(const T& i_val)
  {
  return i_val;
  }

template <typename Val, typename Fn>
Val Apply(const Val& v, Fn fn = Identity<Val>)
  {
  return fn(v);
  }

int main() {
  Apply(42);              // error: no matching function for call to 'Apply(int)'
  Apply(42, Identity<int>); // OK
  return 0;
}

Ответ 1

Поиск функции для вызова состоит из: 1. создание набора кандидатов, который включает в себя вывод аргумента шаблона 2. определение наилучшей перегрузки

Если я правильно понимаю стандарт, в выводе аргументов шаблона принимают участие только фактические аргументы функции (т.е. не по умолчанию). Поэтому из аргумента 42 единственное, что может сделать компилятор, это Val= int. Перегрузка не входит в набор кандидатов, и аргумент по умолчанию никогда не просматривается.

Ответ 2

Вычисление аргумента шаблона не работает таким образом - вы не можете вывести тип аргумента из значения по умолчанию. В С++ 11 вы можете указать аргумент шаблона по умолчанию:

template <typename Val, typename Fn = Val(&)(Val const &)>
Val Apply(const Val& v, Fn fn = Identity<Val>)
{
   return fn(v);
}

Ответ 3

Применить - это шаблонная функция. Вам нужно сделать Apply<MyValueType,MyFuncType>(42);

Вы можете разумно ожидать, что компилятор выведет Val как int, но вы не можете ожидать, что он выведет тип функции, даже если вы указали параметр по умолчанию. В результате этого не будет выведено, что вы пытаетесь вызвать это объявление функции Apply.