Search code examples
c++c++11templatesenable-if

Why does std::is_rvalue_reference not do what it is advertised to do?


For example if I have

#include <type_traits>

struct OwnershipReceiver
{
  template <typename T,
            class = typename std::enable_if
            <
                !std::is_lvalue_reference<T>::value
            >::type
           >
  void receive_ownership(T&& t)
  {
     // taking file descriptor of t, and clear t
  }
};

copied from How to make template rvalue reference parameter ONLY bind to rvalue reference?

the poster uses !std::is_lvalue_reference instead of the immediately more obvious std::is_rvalue_reference. I've verified this in my own code where the former works and the later doesn't.

Can anybody explain why the obvious doesn't work?


Solution

  • Because for forwarding reference, T will never be deduced as an rvalue reference. Suppose passing an object of type int to OwnershipReceiver, if the object is an lvalue, T will be deduced as an lvalue-reference, i.e. int&; if the object is an rvalue, T will be deduced as an non-reference, i.e. int. That's why std::is_rvalue_reference<T>::value won't work because it's always false.

    Note that the purpose of the code is to make sure the parameter type of OwnershipReceiver is an rvalue-reference, it doesn't mean the type of T is an rvalue-reference too.

    In other words, the point here it to distinguish lvalue-reference and non-reference, so !std::is_reference<T>::value works too.


    BTW: If you stick to std::is_rvalue_reference, you can use std::is_rvalue_reference<T&&>::value as you found in the comment, or use it on the parameter t, e.g.

    template <typename T>
    auto receive_ownership(T&& t) -> typename std::enable_if<std::is_rvalue_reference<decltype(t)>::value>::type      
    {
       // taking file descriptor of t, and clear t
    }