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?