Search code examples
c++c++11forwarding

Identity function with perfect forwarding


I want to write a function identity which perfectly forwards its argument without any copy. I would write something like this

 template< typename V >
 inline V&& identity( V&& v )
 {
   return std::forward< V >( v );
 }

But is it correct? Does it return always the correct type? Does it simply forward v independently if it is a lvalue/lvalue reference/temporary?


Solution

  • You can use a parameter pack as a guard, so that nobody can actually force a type that is different from the one that would have been deduced otherwise.
    As a minimal, working example:

    #include <type_traits>
    #include <utility>
    
    template<typename..., typename V>
    constexpr V&& identity(V&& v) {
        return std::forward<V>(v);
    }
    
    int main() {
        static_assert(std::is_same<decltype(identity(42)), int&&>::value, "!");
        static_assert(std::is_same<decltype(identity<int>(42)), int&&>::value, "!");
        static_assert(std::is_same<decltype(identity<float>(42)), int&&>::value, "!");
        // here is the example mentioned by @Jarod42 in the comments to the question
        static_assert(std::is_same<decltype(identity<const int &>(42)), int&&>::value, "!");
    }
    

    This way, the return type depends on the actual type of the v parameter and you cannot force a copy in any way.


    As mentioned in the comments by @bolov, the syntax could quickly become obscure.
    Anyway, as suggested by @Jarod42, it's a matter of being more explicit about what we are doing.

    As an example:

    template <typename... EmptyPack, typename V, typename = std::enable_if_t<sizeof...(EmptyPack) == 0>>
    

    As an alternative:

    template <typename... EmptyPack, typename V, std::enable_if_t<sizeof...(EmptyPack) == 0>* = nullptr>
    

    Or even:

    template <typename... EmptyPack, typename V>
    constexpr
    std::enable_if_t<sizeof...(EmptyPack) == 0, V&&>
    identity(V&& v) {
        return std::forward<V>(v);
    }
    

    Pick your preferred one up and use it, they should be all valid in this case.