1

When passed a parameter, I would like to distinguish between these two cases in a function parameter, like this:

int rvalue();
int&& rvalue_ref();

f(rvalue());
f(rvalue_ref());

However, when I try with forwarding references like this:

int rvalue()
{
    return 1;
}

int&& rvalue_ref(int i)
{
    return std::move(i);
}

template<class T>
void f(T&& x)
{
    if (std::is_rvalue_reference<T>())
    {
        std::cout << "Rvalue reference" << std::endl;
    }
    else if (std::is_lvalue_reference<T>())
    {
        std::cout << "Lvalue reference" << std::endl;
    }
    else
    {
        std::cout << "Not a reference" << std::endl;
    }
}

int main() 
{
    f(rvalue()); // Should print "Not a reference"
    f(rvalue_ref(1)); // Should print "Rvalue reference"
}

It prints out "Not a reference" for both cases. Is there a way to distinguish both cases in C++?

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59

2 Answers2

3

I don't see how to do this solely using function parameter. The distinction between xvalue and prvalue may be lost in the function call.

But you can do it with a macro that calls decltype on the argument, before calling the function. Here is an example that calls your function with the relevant information as a second parameter. I borrowed code from this thread.

#include <iostream>

int rvalue()
{
    return 1;
}

int&& rvalue_ref(int &&i)   // Modified signature to avoid return reference to local variable (ty. user657267)
{
    return std::move(i);
}

template<typename T>
struct value_category {
    // Or can be an integral or enum value
    static constexpr auto value = "prvalue";
};

template<typename T>
struct value_category<T&> {
    static constexpr auto value = "lvalue";
};

template<typename T>
struct value_category<T&&> {
    static constexpr auto value = "xvalue";
};

// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value

#define f(X) f_(X, VALUE_CATEGORY(X)) 

template<class T>
void f_(T&& x, char const *s)
{
    std::cout << s << '\n';
}

int main() 
{
    f(rvalue()); // Should print "Not a reference"
    f(rvalue_ref(1)); // Should print "Rvalue reference"
    int j; f(j);
}

Output:

prvalue
xvalue
lvalue

Of course you can trivially modify the strings to suit, or replace them with enums etc.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
0

The reason why your code said 'Not a reference' is that you passed T to std::std::is_lvalue_reference<> and std::std::is_rvalue_reference<>. You should use decltype() to get its original type.

template<class T>
void f( T&& x )
{
    if ( std::is_lvalue_reference< decltype( x ) >() )
    {
        std::cout << "Lvalue reference" << std::endl;
    }
    else if ( std::is_rvalue_reference < decltype( x ) >() )
    {
         std::cout << "Rvalue reference" << std::endl;
    }
    else
    {
        std::cout << "Not a reference" << std::endl;
    }
}
robotician
  • 42
  • 2