Search code examples
c++language-lawyervalue-categories

Is a function call returning a pointer to an object a prvalue?


Let's imagine this function:

C* get(C* c, int offset) {
    return c + offset;
}

I would like to know if a call to this function is evaluated as a prvalue:

C array_c[3];
C* c2 = get(array_c, 2);

Is get(array_c, 2) a prvalue?

According to Value categories page on cppreference:

  • a glvalue (“generalized” lvalue) is an expression whose evaluation determines the identity of an object, bit-field, or function;
  • a prvalue (“pure” rvalue) is an expression whose evaluation either :
    • computes a value that is not associated with an object
    • creates a temporary object and denotes it
  • an xvalue (an “eXpiring” value) is a glvalue that denotes an object or bit-field whose resources can be reused;
  • an lvalue (so-called, historically, because lvalues could appear on the left-hand side of an assignment expression) is a glvalue that is not an xvalue;

In our example, the expression get(array_c, 2) points to an already exising object that doesn't expire after the call. So we can say it is an lvalue. Moreover, we can write *get(array_c, 2) = C{}; to assign a value to the expression.

However, there are two points that make me think it is not an lvalue. First of all, in the same page of cppreference:

The following expressions are prvalue expressions:

  • a function call or an overloaded operator expression, whose return type is non-reference

In our example, the get function returns a non-reference so, according to this definition, the call should be evaluated as an prvalue.

Also, I'm wondering if get(array_c, 2) is indeed a temporary that makes it a prvalue and *get(array_c, 2) is an lvalue because we can assign a value to it.

What do you think? Is the function call evaluated as an lvalue, a prvalue (or something else)?


Solution

  • get(array_c, 2) and *get(array_c, 2) are two different expressions, and have different value categories.

    get returns pointer by value itself, then get(array_c, 2) is considered as an rvalue (prvalue) expression. You can't perform assignment on it like get(array_c, 2) = nullptr; or get address from it like &get(array_c, 2), it's just same as if get returns other types like int.

    On the other hand, *get(array_c, 2) is an lvalue expression. As you said you can perform assignment on it, which is valid or not, the returned pointer is valid or not doesn't matter.

    *p, the built-in indirection expression;


    PS: Assignment might be performed on rvalues of class type; it's not an absolute condition for determining lvalue expressions or not.