TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #801 4.49K
​​std::type_identity
#опытным

Не так давно мы разбирали функцию std::clamp, которая ограничивает значение переменной верхней и нижней границей:

double increment_speed(double curr_speed, double acceleration, double time_delta) {
curr_speed += acceleration * time_delta;
return std::clamp(curr_speed, kMinSpeed, kMaxSpeed);
}


В таком виде это прекрасно работает. Однако у std::clamp есть одна проблема: все три ее параметра должны быть одного типа:

template<class T>
constexpr const T& clamp( const T& v, const T& lo, const T& hi );


Если попытаться использовать функцию с разными типами, то получим ошибку вывода типов:

auto bounded = std::clamp(42, 3.14, 69.f); // ERROR!


Компилятор не поймет, какой тип Т имелся ввиду, потому что все три аргумента разных типов.

Можно было сделать 3 отдельных параметра:

template<class T1, class T2, class T3>
constexpr const T& clamp( const T1& v, const T2& lo, const T3& hi );


Но тогда приходилось бы навешивать какие-то compile-time проверки совместимости типов.

Есть подход получше - использовать C++20 std::type_identity. Это максимально простая обертка над типом:

template<class T>
struct type_identity { using type = T; };

template< class T >
using type_identity_t = type_identity<T>::type;


Но этот простой финт ушами дает очень важный эффект - отсутствие контекста вывода в шаблонах:

template <class T>
auto bound(T num, typename std::type_identity<T>::type low, typename std::type_identity<T>::type high) {
return std::clamp(num, low, high);
}

auto bounded = bound(25.5, 20, 25);


При использовании зависимых имен(type - зависимое имя шаблонного класса type_identity) компилятор не вывод тип Т для аргументов. Он либо полагается на явное указание аргументов при инстанциации, либо на вывод типа из других параметров. В последнем сниппете только параметр num находится в контексте вывода и по нему компилятор выводит тип Т. Типы параметров low и high не зависят от того, какие соответствующие аргументы мы передаем при вызове функции. Они определяются выведенным типом первого аргумента.

В данном случае тип num выведется в double, поэтому и типы low и high тоже будут double. При вызове просто сработает неявное преобразование от int к double.

Также type_identity можно использовать для того, чтобы запретить вывод типов и заставить пользователя явно прописывать шаблонные параметры. Это может быть важно для точной передачи типа:

template<class T>
void foo(typename std::type_identity<T>::type arg) {}

foo<int>(42); // T жёстко задаётся как int
// foo(42); // Ошибка: вывод T невозможен!


Тоже самое для вариадиков:

template <typename... Ts>
void process(typename std::type_identity<std::tuple<Ts...>>::type data) {
}

process<int, double>(std::tuple{1, 2.0}); // OK
process(std::tuple{1, 2.0}); // ERROR, не указаны типы шаблонных параметров


Прикольный инструмент для тонкой настройки вашего шаблонного кода.

Спасибо @d7d1cd за идею для поста)

Turn off deduction when it is not needed. Stay cool.

#template #cpp20
  • ❤ 29
  • 👍 19
  • 🔥 10
  • 🤯 2
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →