Когда использовать тэг ttt?

Я видел эту мета-функцию, но никогда не понимаю, почему и в каком контексте это требуется. Может ли кто-нибудь объяснить это на примере?

template <typename T>
struct identity
{
    using type = T;
};

Ответ 1

Трюк №1

Предотвращает вычет аргумента шаблона:

template <typename T>
void non_deducible(typename identity<T>::type t) {}

non_deducible(1);      // error
non_deducible<int>(1); // ok

template <typename T>
void first_deducible(T a, typename identity<T>::type b) {}

first_deducible(5, 'A'); // ok

Трюк №2

Отключает небезопасные/нежелательные неявные руководства по вычитанию ():

template <typename T>
struct smart_ptr {
    smart_ptr(typename identity<T>::type* ptr) {}
};

smart_ptr{new int[10]};  // error
smart_ptr<int>{new int}; // ok

Трюк №3

Улучшает определение характеристик типа (и других метафункций):

template <typename T>
struct remove_pointer : identity<T> {};

template <typename T>
struct remove_pointer<T*> : identity<T> {};

Трюк №4

Может использоваться для диспетчеризации тегов:

void foo(identity<std::vector<int>>) {}
void foo(identity<std::list<int>>) {}

template <typename T>
void bar(T t) {
    foo(identity<T>{});
}

Трюк №5

Может использоваться для возвращаемых типов:

template <int I>
constexpr auto foo() {
    if constexpr (I == 0)
        return identity<int>{};
    else
        return identity<float>{};
}

decltype(foo<1>())::type i = 3.14f;

Трюк №6

Помогает специализировать функции, принимающие ссылку на пересылку:

template <typename T, typename U>
void foo(T&& t, identity<std::vector<U>>) {}

template <typename T>
void foo(T&& t) { foo(std::forward<T>(t), identity<std::decay_t<T>>{}); }

foo(std::vector<int>{});

Трюк № 7

Предоставляет альтернативный синтаксис для объявления типов, например указатели/ссылки:

int foo(char);
identity<int(char)>::type* fooPtr = &foo; // int(*fooPtr)(char)

identity<const char[4]>::type& strRef = "foo"; // const char(&strRef)[4]

Трюк № 8

Может использоваться как оболочка для кода, ожидающего существования вложенного T::type или отсрочки его оценки:

struct A {};
struct B { using type = int; };

std::conditional<has_type<A>, A, identity<float>>::type::type; // float
std::conditional<has_type<B>, B, identity<float>>::type::type; // B

Трюк № 9

Раньше он использовался в качестве обходного пути для отсутствующего оператора области действия из decltype():

std::vector<int> v;
identity<decltype(v)>::type::value_type i;
// nowadays one can say just decltype(v)::value_type

identity утилита предложено, чтобы быть добавлены к :

namespace std {
    template <typename T>
    struct type_identity { using type = T; };

    template <typename T>
    using type_identity_t = typename type_identity<T>::type;
}

Ответ 2

Он вводит невыводимый контекст при выводе аргументов шаблона из параметров функции. Например, скажем, у вас есть функция со следующей подписью:

template <class T>
void foo(T a, T b);

Если кто-то должен был вызвать foo(123L, 123), они получили бы ошибку замены, поскольку T не может совпадать с long int и int в то же время.

Если вы хотите совместить только первый аргумент и при необходимости конвертировать другой, неявно, вы можете использовать identity:

template <class T>
void foo(T a, typename identity<T>::type b);

Тогда b не участвует в выводе типа, а foo(123L, 123) разрешается до foo<long int>(123L, 123).