I have seen this meta function used, but never really understod why and in what context it is required. Can someone explain it with an example?
template <typename T>
struct identity
{
using type = T;
};
Prevents template argument deduction:
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
Disables unsafe/unwanted implicit deduction guides (c++17):
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
Makes defining type-traits (and other metafunctions) easier:
template <typename T>
struct remove_pointer : identity<T> {};
template <typename T>
struct remove_pointer<T*> : identity<T> {};
Can be used for tag dispatching:
void foo(identity<std::vector<int>>) {}
void foo(identity<std::list<int>>) {}
template <typename T>
void bar(T t) {
foo(identity<T>{});
}
Can be used for returning types:
template <int I>
constexpr auto foo() {
if constexpr (I == 0)
return identity<int>{};
else
return identity<float>{};
}
decltype(foo<1>())::type i = 3.14f;
Helps to specialize functions accepting a forwarding-reference:
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>{});
Provides alternative syntax for declaring types, e.g. pointers/references:
int foo(char);
identity<int(char)>::type* fooPtr = &foo; // int(*fooPtr)(char)
identity<const char[4]>::type& strRef = "foo"; // const char(&strRef)[4]
Can be used as a wrapper for code expecting nested T::type
to exist or deferring its evaluation:
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
In the past, it used to serve as a workaround for a missing scope operator from the decltype()
specifier:
std::vector<int> v;
identity<decltype(v)>::type::value_type i;
// nowadays one can say just decltype(v)::value_type
The identity
utility is proposed to be added to c++20:
namespace std {
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using type_identity_t = typename type_identity<T>::type;
}
It introduces a non-deduced context, when deducing template arguments from function parameters. For example, say you have a function with the following signature :
template <class T>
void foo(T a, T b);
If someone were to call foo(123L, 123)
, they'd get a substitution error, as T
cannot match long int
and int
at the same time.
If you want to match only the first argument, and convert the other one implicitly if needed, you can use identity
:
template <class T>
void foo(T a, typename identity<T>::type b);
Then b
does not participate in type deduction, and foo(123L, 123)
resolves to foo<long int>(123L, 123)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With