什么时候使用"身份"tmp技巧?

0xB*_*F00 18 c++

我已经看到过使用这个元函数,但从未真正理解为什么以及在什么情况下需要它.有人能用一个例子解释一下吗?

template <typename T>
struct identity
{
    using type = T;
};
Run Code Online (Sandbox Code Playgroud)

Pio*_*cki 27

技巧#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
Run Code Online (Sandbox Code Playgroud)

技巧#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
Run Code Online (Sandbox Code Playgroud)

技巧#3

使定义类型特征(和其他元函数)更容易:

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

template <typename T>
struct remove_pointer<T*> : identity<T> {};
Run Code Online (Sandbox Code Playgroud)

特技#4

可用于标签调度:

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

template <typename T>
void bar(T t) {
    foo(identity<T>{});
}
Run Code Online (Sandbox Code Playgroud)

技巧#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;
Run Code Online (Sandbox Code Playgroud)

技巧#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>{});
Run Code Online (Sandbox Code Playgroud)

技巧#7

提供声明类型的替代语法,例如指针/引用:

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

identity<const char[4]>::type& strRef = "foo"; // const char(&strRef)[4]
Run Code Online (Sandbox Code Playgroud)

技巧#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
Run Code Online (Sandbox Code Playgroud)

特技#9

过去,它曾用作decltype()说明符中缺少范围运算符的变通方法:

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

建议将该identity实用程序添加到:

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

    template <typename T>
    using type_identity_t = typename type_identity<T>::type;
}
Run Code Online (Sandbox Code Playgroud)


Que*_*tin 16

当从函数参数中推导出模板参数时,它会引入一个非推导的上下文.例如,假设您有一个具有以下签名的函数:

template <class T>
void foo(T a, T b);
Run Code Online (Sandbox Code Playgroud)

如果有人打电话foo(123L, 123),他们会得到替换错误,因为T无法匹配long int并且int同时也是如此.

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

template <class T>
void foo(T a, typename identity<T>::type b);
Run Code Online (Sandbox Code Playgroud)

然后b不参与类型演绎,并foo(123L, 123)解决foo<long int>(123L, 123).