7
votes

In my effort to understand rvalue references, I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.

(This issue is related to reference collapsing; see Concise explanation of reference collapsing rules requested: (1) A& & -> A& , (2) A& && -> A& , (3) A&& & -> A& , and (4) A&& && -> A&&).

In particular, I have been considering if the compiler will always treat unnamed objects as rvalue references and/or if the compiler will always treat temporary objects as rvalue references.

In turn, this leads me to question whether unnamed objects are equivalent to temporary objects.

My question is: Are unnamed objects always temporary; and are temporary objects always unnamed?

In other words: Are unnamed objects and temporary objects equivalent?

3
I don't know the technical terminology well enough to post as an answer, but I think this is a counter-example: void foo(int) {}. Here the parameter is an object that is not temporary, but has no name. - GManNickG
Good point. This example seems to make clear that it is temporary objects that are relevant to rvalue references - not so much unnamed variables. - Dan Nissenbaum
Is there actually a definition for "unnamed object?" - Nikos C.
I would like to know the definition of "temporary object" (as well...) - Dan Nissenbaum

3 Answers

2
votes

I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.

I assume you are talking about function templates with universal reference parameters, like this?

template<typename T>
void foo(T&& t)
{
}

The rules are very simple. If the argument is an rvalue of type X, then T will be deduced to be X, hence T&& means X&&. If the argument is an lvalue of type X, then T will be deduced to be X&, hence T&& means X& &&, which is collapsed into X&.

If you were really asking about arguments, then the question does not make much sense, because arguments are never lvalue references or rvalue references, because an expression of type X& is immediately converted to an expression of type X, which denotes the referenced object.

But if you actually meant "How does the compiler distinguish lvalue arguments from rvalue arguments?" (note the missing reference), then the answer is simple: the compiler knows the value category of every expression, because the standard specifies for every conceivable expression what its value category is. For example, the call of a function is an expression that can belong to one of three value categories:

X   foo();   // the expression foo() is a prvalue
X&  bar();   // the expression bar() is an lvalue
X&& baz();   // the expression baz() is an xvalue

(Provided, of course, that X itself is not a reference type.)

If none of this answers your question, please clarify the question. Also, somewhat relevant FAQ answer.

3
votes

I might be wrong, since I'm not sure what the definition of "unnamed object" is. But consider the argument of the foo() function below:

void foo(int)
{ /* ... */ }

int main()
{ foo(5); }

foo()'s argument is unnamed, but it's not a temporary. Therefore, unnamed objects and temporary objects are not equivalent.

3
votes

Temporary objects can be named.

Very common case - when passed as a parameter to a function. Another less common case - binding a const reference to an rvalue result of a function.

int f(int i) { return i + 1; }
int g() { const int &j = f(1); return j; }

Unnamed objects are often temporary, but not always. For example - anonymous union object:

struct S
{
   union { int x; char y; };
} s;

And, of course, any object created by operator new.

Perhaps there are other cases, but even only these can serve as counterexamples to the hypothesis :)