Are All Temporaries Rvalues in C++

Are all temporaries rvalues in C++?

No.

The C++ language specification never makes such a straightforward assertion as the one you are asking about. It doesn't say anywhere in the language standard that "all temporary objects are rvalues". Moreover, the question itself is a bit of misnomer, since the property of being an rvalue in the C++ language is not a property of an object, but rather a property of an expression (i.e. a property of its result). This is actually how it is defined in the language specification: for different kinds of expressions it says when the result is an lvalue and when it is an rvalue. Among other things, this actually means that a temporary object can be accessed as an rvalue as well as an lvalue, depending on the specific form of expression that is used to perform the access.

For example, the result of literal 2 + 3 expression is obviously an rvalue, a temporary of type int. We cannot apply the unary & to it since unary & requires an lvalue as its operand

&(2 + 3); // ERROR, lvalue required

However, as we all know, a constant reference can be attached to a temporary object, as in

const int &ri = 2 + 3;

In this case the reference is attached to the temporary, extending the lifetime of the latter. Obviously, once it is done, we have access to that very same temporary as an lvalue ri, since references are always lvalues. For example, we can easily and legally apply the unary & to the reference and obtain a pointer to the temporary

const int *pi = &ri;

with that pointer remaining perfectly valid as long as the temporary persists.

Another obvious example of lvalue access to a temporary object is when we access a temporary object of class type through its this pointer. The result of *this is an lvalue (as is always the case with the result of unary * applied to a data pointer), yet it doesn't change the fact that the actual object might easily be a temporary. For a given class type T, expression T() is an rvalue, as explicitly stated in the language standard, yet the temporary object accessed through *T().get_this() expression (with the obvious implementation of T::get_this()) is an lvalue. Unlike the previous example, this method allows you to immediately obtain a non-const-qualified lvalue, which refers to a temporary object.

So, once again, the very same temporary object might easily be "seen" as an rvalue or as an lvalue depending on what kind of expression (what kind of access path) you use to "look" at that object.

What are C++ temporaries?

In C++ temporaries are unnamed objects that compiler creates in various contexts. The typical uses include reference initialization, argument passing, evaluation of expressions (including standard type conversions), function returns, and exceptions (throw expressions).

As from this link:

When a temporary object is created to initialize a reference variable, the name of the temporary object has the same scope as that of the reference variable. When a temporary object is created during the evaluation of a full-expression (an expression that is not a subexpression of another expression), it is destroyed as the last step in its evaluation that lexically contains the point where it was created.

There are exceptions in the destruction of full-expressions:

  1. The expression appears as an initializer for a declaration defining an object: the temporary object is destroyed when the initialization is complete.
  2. A reference is bound to a temporary object: the temporary object is destroyed at the end of the reference's lifetime.

If a temporary object is created for a class with constructors, the compiler calls the appropriate (matching) constructor to create the temporary object.

When a temporary object is destroyed and a destructor exists, the compiler calls the destructor to destroy the temporary object. When you exit from the scope in which the temporary object was created, it is destroyed. If a reference is bound to a temporary object, the temporary object is destroyed when the reference passes out of scope unless it is destroyed earlier by a break in the flow of control. For example, a temporary object created by a constructor initializer for a reference member is destroyed on leaving the constructor.

In cases where such temporary objects are redundant, the compiler does not construct them, in order to create more efficient optimized code. This behavior could be a consideration when you are debugging your programs, especially for memory problems.

Let us summarize it this way. These temporary objects can be created for the following reasons:

  1. To initialize a const reference with an initializer of a type different from that of the underlying type of the reference being initialized.

  2. To store the return value of a function that returns a user-defined type. These temporaries are created only if your program does not copy the return value to an object. Because the return value is not copied to another object, a temporary object is created. A more common case where temporaries are created is during the evaluation of an expression where overloaded operator functions must be called. These overloaded operator functions return a user-defined type that often is not copied to another object.

  3. To store the result of a cast to a user-defined type. When an object of a given type is explicitly converted to a user-defined type, that new object is constructed as a temporary object.

Let's consider the example:

class X {
/ / ...
public:
/ / ...
X(int);
X(const X&);
~X();
};

X f(X);

void g()
{
X a(1);
X b = f(X(2));
a = f(a);
}

Here, an implementation might use a temporary in which to construct X(2) before passing it to f() using X’s copy-constructor; alternatively, X(2) might be constructed in the space used to hold the argument. Also, a temporary might be used to hold the result of f(X(2)) before copying it to b using X’s copy-constructor; alternatively, f()’s result might be constructed in b. On the other hand, the expression a=f(a) requires a temporary for the result of f(a), which is then assigned to a.

Rvalues vs temporaries

Temporaries and rvalues are different (but related) concepts. Being temporary is a property of an object. Examples of objects that aren't tempory are local objects, global objects and dynamically created objects.

Being an rvalue is a property of an expression. The opposite of rvalues are lvalues such as names or dereferenced pointers. The statement "Temporaries are rvalues" is meaningless. Here is the relationsip between rvalues and temporary objects:

An rvalue is an expression whose evaluation creates a temporary object which is destroyed at the end of the full-expression that lexically contains the rvalue.

Note that lvalues can also denote temporary objects!

void blah(const std::string& s);

blah(std::string("test"));

Inside the function blah, the lvalue s denotes the temporary object created by evaluating the expression std::string("test").

Your comment "references are lvalues" is also meaningless. A reference is not an expression and thus cannot be an lvalue. What you really mean is:

The expression function() is an lvalue if the function returns a reference.

rvalue references: what exactly are temporary objects, what is their scope, and where are they stored?

Firstly it is important not to conflate the terms "rvalue" and "temporary object". They have very different meanings.


Temporary objects do not have a storage duration. Instead, they have lifetime rules that are specific to temporary objects. These can be found in section [class.temporary] of the C++ Standard; there is a summary on cppreference, which also includes a list of which expressions create temporary objects.

In practice I'd expect that a compiler would either optimize the object out, or store it in the same location as automatic objects are stored.

Note that "temporary object" only refers to objects of class type. The equivalent for built-in types are called values. (Not "temporary values"). In fact the term "values" includes both values of built-in type, and temporary objects.

A "value" is a completely separate idea to prvalue, xvalue, rvalue. The similarity in spelling is unfortunate.

Values don't have scope. Scope is a property of a name. In many cases the scope of a name coincides with the lifetime of the object or value it names, but not always.


The terms rvalue, lvalue etc. are value categories of an expression. These describe expressions, not values or objects.

Every expression has a value category. Also, every expression has a value, except expressions of void type. These are two different things. (The value of an expression has a non-reference type.)

An expression of value category rvalue may designate a temporary object, or a non-temporary object, or a value of built-in type.

The expressions which create a temporary object all have value category prvalue, however it is then possible to form expressions with category lvalue which designate that same temporary object. For example:

const std::string &v = std::string("hello");

In this case v is an lvalue, but it designates a temporary object. The lifetime of this temporary object matches the lifetime of v, as described in the earlier cppreference link.

Link to further reading about value categories


An rvalue reference is a reference that can only bind to an expression of value category rvalue. (This includes prvalue and xvalue). The word rvalue in its name refers to what it binds to, not its own value category.

All named references in fact have category lvalue. Once bound, there is no difference in behaviour between an rvalue reference and an lvalue reference.

std::string&& rref = std::string("hello");

rref has value category lvalue , and it designates a temporary object. This example is very similar to the previous one, except the temporary object is non-const this time.

Another example:

std::string s1("hello");
std::string&& rref = std::move(s1);
std::string& lref = s1;

In this case, rref is an lvalue, and it designates a non-temporary object. Further, lref and rref (and even s1!) are all indistinguishable from hereon in, except for specifically the result of decltype.

Is it necessary to have a temporary or a literal to have an rvalue?

Expressions that yield temporary objects are r-values. There's a special rule which allows const-references and r-value references to bind to r-values, and this extends the lifetime of the temporary object to that of the reference (see 12.2(5)), but that does not make the temporary-object expression any less of an r-value.

However, once bound to a reference, the reference variable itself has a name, and thus the reference expression is an l-lvalue.

Don't confuse expressions, variables and objects.

C++0x rvalue references and temporaries

GCC is doing it wrong according the FCD. The FCD says at 8.5.3 about reference binding

  • If the reference is an lvalue reference and the initializer expression is an [lvalue / class type] ...
  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type.

Your case for the call to the std::string && matches none of them, because the initializer is an lvalue. It doesn't get to the place to create a temporary rvalue, because that toplevel bullet already requires an rvalue.

Now, overload resolution doesn't directly use reference binding to see whether there exist an implicit conversion sequence. Instead, it says at 13.3.3.1.4/2

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1.

Thus, overload resolution figures out a winner, even though that winner may actually not be able to bind to that argument. For example:

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&);
void f(B);
int main() { A a; f(a.bits); }

Reference binding at 8.5 forbids bitfields to bind to lvalue references. But overload resolution says that the conversion sequence is the one converting to int, thus succeeding even though when the call is made later, the call is ill-formed. Thus my bitfields example is ill-formed. If it was to choose the B version, it would have succeeded, but needed a user defined conversion.

However, there exist two exceptions for that rule. These are

Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference to an lvalue.

Thus, the following call is valid:

struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };

void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }

And thus, your example calls the const T& version

void f(const std::string &);
void f(std::string &&); // would bind to lvalue!

void g(const char * arg) { f(arg); }

However, if you say f(arg + 0), you create an rvalue, and thus the second function is viable.

If temporaries are implicitly non-modifiable, how does this work?

I'm told that, in C++03, temporaries are implicitly non-modifiable.

That is not correct. Temporaries are created, among other circumstances, by evaluating rvalues, and there are both non-const rvalues and const rvalues. The value category of an expression and the constness of the object it denotes are mostly orthogonal 1. Observe:

      std::string foo();
const std::string bar();

Given the above function declarations, the expression foo() is a non-const rvalue whose evaluation creates a non-const temporary, and bar() is a const rvalue that creates a const temporary.

Note that you can call any member function on a non-const rvalue, allowing you to modify the object:

foo().append(" was created by foo")   // okay, modifying a non-const temporary
bar().append(" was created by bar") // error, modifying a const temporary

Since operator= is a member function, you can even assign to non-const rvalues:

std::string("hello") = "world";

This should be enough evidence to convince you that temporaries are not implicitly const.

1: An exception are scalar rvalues such as 42. They are always non-const.

Where is the rvalue coming from?

Is there a temporary variable that contains the double value of a that was cast from float? And is this temporary then the rvalue?

Yes. left and right with type double& can't bind to floats directly. They have to be converted to doubles firstly, which are temporaries (rvalues) and can't be bound to lvalue-reference to non-const.

If you change the type of left and right to const double& then it'll work fine. The converted double temporaries (rvalues) could be bound to lvalue-reference to const.



Related Topics



Leave a reply



Submit