何时使用"identity" tmp 技巧?

When to use the "identity" tmp trick?

本文关键字:技巧 tmp identity 何时使      更新时间:2023-10-16

我见过使用这个元函数,但从未真正理解为什么以及在什么上下文中需要它。有人能举个例子解释一下吗?

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

技巧#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

禁用不安全/不需要的隐式演绎指南(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

技巧# 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

建议在c++20中增加identity实用程序:

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

在从函数形参推导模板实参时引入了一个非推导的上下文。例如,假设您有一个签名如下的函数:

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

如果有人调用foo(123L, 123),他们会得到替换错误,因为T不能同时匹配long intint

如果您只想匹配第一个参数,并在需要时隐式转换另一个参数,您可以使用identity:

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

b不参与类型演绎,foo(123L, 123)解析为foo<long int>(123L, 123)