Вывод типа шаблона в C++

Когда мы говорим о C++, auto должно быть привлекательной функцией. Тем не менее, чтобы понять вывод типа auto, мы должны сначала взглянуть на template type deduction. Содержание этого поста заимствовано из 'Пункт 1. Понимание вывода типа шаблона' в Effective Modern C++.

Вывод типа шаблона происходит, когда аргументы шаблона не указаны явно. Ниже приведен один из простых примеров, показывающих явно заданные аргументы шаблона.

template<typename T>
void f(const T& param)
int x = 0; 
f<double>(x);

Чтобы подробнее объяснить вывод типа шаблона, мы будем использовать два ключевых слова: T и ParameterType, как показано ниже.

template<typename T>
void f(ParameterType param)
  • T — тип аргумента шаблона, а ParameterType — тип аргумента функции param.
  • T и ParameterType могут быть, но не обязательно одинаковыми.

Для вызова функции f(expr) ниже

template<typename T>
void f(const T& param)
int x = 0;
f(x);

T выводится как int, а ParameterType выводится как const int &.

Чтобы вывести тип для T, он зависит не только от типа expr, но и от формы ParameterType.

Есть три случая, когда

  • ParameterType — это тип указателя или ссылки, но не универсальная ссылка
  • ParameterType — это универсальная ссылка
  • ParameterType не является ни указателем, ни ссылкой

ParameterType — это тип указателя или ссылки, но не универсальная ссылка.

template<typename T>
void f1(T&) {
  std::cout << "T is ";
  if (std::is_const_v<T>) {
    std::cout << "const ";
  }
  std::cout << typeid(T).name() << std::endl;
}

Если теперь мы запустим код

int main() {
  int x = 27;
  const int cx = x;
  const int& rx = x;
  f1(x);
  f1(cx);
  f1(rx);
}

Результат будет

T is i
T is const i
T is const i

Справочность rx игнорируется во время вывода типа, и это работает так же для указателя.

Если мы явно напишем const для аргумента функции шаблона, давайте проверим, что T будет выведено как

template <typename T>
void f2(const T&) {
 std::cout << "T is ";
 if (std::is_const_v<T>) {
   std::cout << "const ";
 } 
 std::cout << typeid(T).name() << std::endl;
}

Если мы снова запустим код, используя f2, вывод будет

T is i
T is i
T is i

Для трех случаев T выводится как int, другими словами, вызываемая функция на самом деле void f2(const int &).

ParameterType — универсальная ссылка

  • Для универсальной справки мы можем использовать совершенную пересылку, чтобы проверить, какой тип функции вызывается. Пожалуйста, обратитесь к моему предыдущему сообщению Идеальная переадресация
  • Теперь давайте воспользуемся идеальной пересылкой вместе с некоторой перегрузкой функций, чтобы проверить, какой тип функции вызывается.
void f(int&) { std::cout << "calling f(int &)" << std::endl; }
void f(const int&) { 
 std::cout << "calling f(const int &)" << std::endl;
}
void f(int&&) { std::cout << "calling f(int &&)" << std::endl; }
template <typename T>
void f3(T&& param) {
  f(std::forward<T>(param));
}

Если мы выполним код

int main() {
  int x = 27; 
  const int cx = x; 
  const int& rx = x; 
  f3(x);
  f3(cx);
  f3(rx);
  f3(27);
}

Мы получим вывод

calling f(int &)
calling f(const int &)
calling f(const int &)
calling f(int &&)

Ценность и ценность остались.

ParameterType не является ни указателем, ни ссылкой

template <typename T>
void f4(T) {
  std::cout << "T is ";
  if (std::is_const_v<T>) {
   std::cout << "const "; 
  }
  std::cout << typeid(T).name() << std::endl;
}

Когда он передается по значению, const, volatile и ссылка игнорируются.

Если мы снова запустим код, используя f4, вывод будет

T is i
T is i
T is i

Спасибо за прочтение! Код доступен здесь. Надеюсь, теперь вы лучше понимаете вывод типа шаблона.