I have been delving into the intricacies of C++ recently, and there's a specific behavior that has puzzled me: the rules and behaviors around temporaries, especially regarding taking their address and assigning to them.
Assignment to Temporaries: Furthermore, I observed that I can assign to non-const temporaries of UDTs:
Rational a(1, 2), b(3, 4), c(5, 6);
(a * b) = c; // This line compiles
However, attempting to assign to the result of a function returning by value (which is an r-value) with built-in types leads to a compile error:
int f(int x, int y) {
return x;
}
int main() {
f(10, 5) = 12; // This line won’t compile
}
My Questions: Assignment to Temporaries: How is it possible to assign to a non-const temporary of a UDT? Is this behavior consistent, and should it be used or avoided in practice? I've searched through various resources and haven't found a satisfactory explanation for these behaviors. Could someone please shed some light on these issues?
Thank you in advance!
The sole reason is that the built-in operators have certain requirements when it comes to the value category of the operand.
The assignment operator (
=
) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; [...]
The operand of the unary
&
operator shall be an lvalue of some typeT
.
Fr example, &4
isn't valid because 4
is a prvalue.
Operator overloads can bypass these rules because &some_rational
simply translates into a function call. If Rational
has an overloaded &
operator, you can do &Rational{}
which would take the address of a prvalue. If Rational
has an overloaded =
operator (all class types do, actually), you can do Rational{} = ...
which assigns to a prvalue. These expression aren't possible for the built-in &
and =
operator.
Note that you can disable this behavior using ref-qualifiers:
struct Rational {
/* ... */
// the & ref-qualifier means that the = operator only works with lvalues
Rational& operator=(const Rational& other) &;
};
Since const&
can bind to temporary objects, it is not possible to achieve the same for a member function with a const
qualifier. The closest you could get is using C++23's explicit object parameters:
template <std::same_as<const Rational> Self>
address_type operator&(this Self auto& r);