Почему int * оценивается как int * вместо int * * при подстановке в шаблонный параметр?

При подстановке указателя типа int * в шаблонный параметр T, он оценивается как int *, а не int **. Это происходит из-за правила, называемого правилом дедукции указателей в языке C++. Правило дедукции указателей гласит, что тип T*, где T - это некоторый тип, дедуцируется как U*, где U - это некоторый другой тип. Он не дедуцируется как U** или что-то другое.

Причина такого поведения заключается в том, что указатели в C++ обеспечивают определенную гибкость и могут быть использованы для различных целей, таких как передача аргументов функции по ссылке или возвращение адреса переменной из функции. Поэтому, если шаблон ожидает указатель на определенный тип T, то при передаче int * он должен принять его как int *, чтобы обеспечить совместимость типов.

Другими словами, шаблоны в C++ выполняют дедукцию типа для аргументов шаблона, чтобы установить типы, необходимые для корректной инстанциации шаблона. Однако дедукция типа не производится рекурсивно через указатели или другие сложные типы данных, а только для простых типов данных.

Если вы хотите передать указатель на указатель int ** в шаблонный параметр в C++, вам следует использовать явную специализацию шаблона для этого типа данных. Например:

template<typename T>
void foo(T* value) {
  // ...
}

template<>
void foo<int**>(int** value) {
  // ...
}

int main() {
  int* ptr = nullptr;
  int** ptr2 = &ptr;
  
  foo(ptr2); // вызывается специализированный шаблон для типа int**
  
  return 0;
}

В этом примере мы определяем функцию foo с обобщенным шаблонным параметром T*, а затем определяем явную специализацию функции foo для типа int**. Специализированная функция будет вызвана, когда передается аргумент типа int**.