When to use the “identity” tmp trick?

前端 未结 2 885
感情败类
感情败类 2020-12-13 20:15

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 <         


        
相关标签:
2条回答
  • 2020-12-13 20:51

    Trick #1

    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
    

    Trick #2

    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
    

    Trick #3

    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> {};
    

    Trick #4

    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>{});
    }
    

    Trick #5

    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;
    

    Trick #6

    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>{});
    

    Trick #7

    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]
    

    Trick #8

    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
    

    Trick #9

    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;
    }
    
    0 讨论(0)
  • 2020-12-13 21:06

    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).

    0 讨论(0)
提交回复
热议问题