Here is some example code:
#include <iostream>
class Foo
{
public:
explicit Foo(int x) : data(x) {};
Foo& operator++()
{
data += 1;
return *this;
}
void *get_addr()
{
return (void*)this;
}
friend Foo operator + (const Foo& lhs, const Foo& rhs);
friend std::ostream& operator << (std::ostream& os, const Foo& f);
private:
int data;
};
std::ostream& operator << (std::ostream& os, const Foo& f)
{
return (os << f.data);
}
Foo operator + (const Foo& lhs, const Foo& rhs)
{
return Foo(lhs.data + rhs.data);
}
void bar(Foo& f)
{
std::cout << "bar(l-value ref)" << std::endl;
}
void bar(const Foo& f)
{
std::cout << "bar(const l-value ref)" << std::endl;
}
void bar(Foo&& f)
{
std::cout << "bar(r-value ref)" << std::endl;
}
int main()
{
// getting the identity of the object
std::cout << Foo(5).get_addr() << std::endl; // Can write &Foo(5)
// by overloading &
// overload resolution
bar(Foo(5)); // prints r-value ref
// default copy assignment
std::cout << (Foo(78) = Foo(86)) << std::endl; // prints 86
// mutating operations
std::cout << (++Foo(5)) << std::endl; // prints 6
// more mutating operations
std::cout << (++(Foo(78) + Foo(86))) << std::endl; // prints 165
// overload resolution
bar((Foo(78) + Foo(86))); // prints r-value ref
}
Are expressions like Foo(5) prvalues or general rvalues? Does the fact that I can call get_addr() on these expressions mean that they have identity? Or does the fact that I cannot apply the default &-operator (I mean non-overloaded) mean that they do not have identity and are therefore prvalues?
Is it also fair to say that mutability of the produced value via the expression that produced it is orthogonal to this value-classification?
Every expression is one, and only one, of:
The union of expressions that are lvalues and xvalues are known collectively as glvalues.
The union of expressions that are xvalues and prvalues are known collectively as rvalues.
Thus xvalue expressions are known both as glvalues and rvalues.
The handy diagram found in Alf's answer correctly illustrates the relationship I've described with words above, and is also found in section 3.10 of the C++ standards, versions C++11 and above.
Everything I've said above, I suspect the OP already knew, just from the wording of the title of this question.
Trivia:
What I'm adding is a way to discover for yourself which of the three bottom classification categories any expression falls into: lvalue, xvalue or prvalue.
The above function can be used like:
And it will answer this OP's question. For example:
For me prints out:
One area to be careful of in the use of this function:
decltype(variable_name)
will give the declared type of the variable name. If you want to discover the value category of the expression whenvariable_name
is used (as opposed to its declared type), then you need to add extra parentheses around(variable_name)
when used indecltype
. That is:is the type of the expression
variable_name
, and not the declared type ofvariable_name
.For example given:
This will erroneously output:
Add the extra parentheses to the
decltype
:to convert
foo
from a type name into an expression. Now the output is:If you are unsure whether you need to add parentheses or not to get the correct answer, then just add them. Adding them won't make a correct answer wrong -- unless you are looking to get the declared type of a variable, and not the type of an expression. And in that latter case, you want a closely related function:
type_name<T>()
.