Search code examples
c++c++17rvalue-referencervaluelvalue

If I accept a parameter via universal reference, is exactly one of is_rvalue_reference and is_lvalue_reference true?


Is it ever possible for this code to print "neither"?

using namespace std;
template<typename T>
void foo(T&& t) {
    if constexpr (is_lvalue_reference_v<T>) {
        cout << "lv" << endl;
    } else if constexpr (is_rvalue_reference_v<T>) {
        cout << "rv" << endl;
    } else {
        cout <<"neither" << endl;
    }
}

Solution

  • Is it ever possible for this code to print "neither"?

    Yes, "neither" will be printed any time an rvalue is passed to foo and no explicit template parameter is given:

    foo(42);  // "neither" is printed because T is deduced as int
    

    Or when a non-reference type is explicitly specified:

    int i=0;
    // "neither" is printed because T is explicitly specified as int:
    foo<int>(std::move(i));
    

    While T can be a non-reference type, the type of t will always be a reference type. There are three possibilities for the type of t:

    1. T is a value type (i.e. int): the type of t is int&&; rvalue-reference to int.
    2. T is an lvalue-reference (i.e. int&): the type of t is int& &&, which collapses to int&; lvalue-reference to int.
    3. T is an rvalue-reference (i.e. int&&): the type of t is int&& &&, which collapses to int&&; rvalue-reference to int.

    This is the mechanism by which forwarding references work. If you pass an rvalue to foo, then T will be deduced to be a value type. If you pass an lvalue then T will be deduced to be an lvalue-reference type.