Search code examples
c++stdtype-traitsc++-templates

Purpose of using type_identity in template function trailing return type


What is the purpose of using type_identity as trailing return type?

https://en.cppreference.com/w/cpp/types/add_reference provided following possible implementation for add_lvalue_reference & add_rvalue_reference

namespace detail {
 
template <class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C++20)
 
template <class T> // Note that `cv void&` is a substitution failure
auto try_add_lvalue_reference(int) -> type_identity<T&>;
template <class T> // Handle T = cv void case
auto try_add_lvalue_reference(...) -> type_identity<T>;
 
template <class T>
auto try_add_rvalue_reference(int) -> type_identity<T&&>;
template <class T>
auto try_add_rvalue_reference(...) -> type_identity<T>;
 
} // namespace detail
 
template <class T>
struct add_lvalue_reference
    : decltype(detail::try_add_lvalue_reference<T>(0)) {};
 
template <class T>
struct add_rvalue_reference
    : decltype(detail::try_add_rvalue_reference<T>(0)) {};

I have tried my own implementation without using type_identity and it works as expected. Did I miss something?

namespace detail {

template <class T> // Note that `cv void&` is a substitution failure
auto try_add_lvalue_reference(int) -> T&;


template <class T> // Handle T = cv void case
auto try_add_lvalue_reference(...) -> T;

template <class T>
auto try_add_rvalue_reference(int) -> T&&;
template <class T>
auto try_add_rvalue_reference(...) -> T;

} // namespace detail

template <class T>
struct add_lvalue_reference {
    using type = decltype(detail::try_add_lvalue_reference<T>(0));
};

template <class T>
struct add_rvalue_reference {
    using type = decltype(detail::try_add_rvalue_reference<T>(0));
};

int main()
{
    if (std::is_same_v<add_lvalue_reference<int>::type, int&>) {
        std::cout << "add_lvalue_reference int same\n";
    } else {
        std::cout << "add_lvalue_reference int different\n";
    }

    if (std::is_same_v<add_lvalue_reference<void>::type, void>) {
        std::cout << "add_lvalue_reference void same\n";
    } else {
        std::cout << "add_lvalue_reference void different\n";
    }

    if (std::is_same_v<add_rvalue_reference<int>::type, int&&>) {
        std::cout << "add_rvalue_reference int same\n";
    } else {
        std::cout << "add_rvalue_reference int different\n";
    }

    if (std::is_same_v<add_rvalue_reference<void>::type, void>) {
        std::cout << "add_rvalue_reference void same\n";
    } else {
        std::cout << "add_rvalue_reference void different\n";
    }

    return 0;
}

Solution

  • Your implementation fails for cv-qualified void and trying to add rvalue reference to a function type or rvalue-reference to a function type (Since the decltype(function-returning-cv-void()) is unqualified void and decltype(function-returning-rvalue-reference-to-function()) is an lvalue reference to said function type, since there are no function rvalues).

    And some types are not referenceable and also cannot be returned from functions, like cv-qualified function types.

    // Your functions
     add_lvalue_reference<const void>
      -> void
     add_rvalue_reference<int(int param)>
      -> int(&)(int param)
     add_rvalue_reference<int(&&)(int param)>
      -> int(&)(int param)
     add_lvalue_reference<int(int param) const>
      (fails to compile)
    
    // Expected types
     add_lvalue_reference<const void>
      -> const void
     add_rvalue_reference<int(int param)>
      -> int(&&)(int param)
     add_rvalue_reference<int(&&)(int param)>
      -> int(&&)(int param)
     add_lvalue_reference<int(int param) const>
      -> int(int param) const