4

In below code snippet, why line 2 + 3 = 5 statement gives error but next statement of assigning to string concatenation compiles successfully?

#include <string>

int main() {                                                                                                                                                  
   2 + 3 = 5;   //  Error: lvalue required as left operand of assignment                                                                                                                                             
   std::string("2") + std::string("3") = std::string("5");  // Compiles successfully. why?                                                                                                 
   return 0;                                                                                                                                                 
}

My understanding is that left hand side of the expression std::string("2") + std::string("3") = std::string("5") will produce temporary which is rvalue. That means I am assigning to rvalue - just like 2 + 3 = 5. So it should also give lvalue required as left operand of assignment error. But it does not.

UnSat
  • 1,347
  • 2
  • 14
  • 28
  • 1
    Simplification `std::string("3") = std::string("5");` still compiles – Richard Critten Nov 02 '19 at 23:47
  • 1
    Possible duplicate of [C++ function returns a rvalue, but that can be assigned a new value?](https://stackoverflow.com/questions/15824750/c-function-returns-a-rvalue-but-that-can-be-assigned-a-new-value) – Davis Herring Nov 02 '19 at 23:51
  • 1
    Just checked most of the standard containers and non of them have the the rvalue version of `operator=` deleted eg `basic_string& basic_string::operator=( CharT ch ) && = delete;` is not done for any of the containers. So I guess it is a conscious decision. – Richard Critten Nov 02 '19 at 23:56

2 Answers2

3

Explanation

For class types, assignment is implemented by copy and move assignment operators. std::string is a class, so

std::string("2") + std::string("3") = std::string("5")

is just syntactic sugar for

(std::string("2") + std::string("3")).operator=(std::string("5"))

operator= is a member function. Usually, a member function can be called on both lvalues and rvalues. Therefore, this expression is valid.

Standard reference

For non-overloaded operator= (i.e., for int): [expr.ass]/1

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. [...]

For overload operator= (for std::string): [expr.ass]/4

If the left operand is of class type, the class shall be complete. Assignment to objects of a class is defined by the copy/move assignment operator ([class.copy], [over.ass]).

(emphasis mine for all)

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 1
    It gets weirder in that the assignment returns an lvalue, not an xvalue. – Davis Herring Nov 02 '19 at 23:52
  • 2
    @DavisHerring Yes. I'd say that `std::string` wasn't designed for rvalue assignment in the first place, so it doesn't bother propagating value category. – L. F. Nov 02 '19 at 23:55
  • 1
    @L.F. it's all the std containers as far as I can tell – Richard Critten Nov 02 '19 at 23:58
  • 1
    @RichardCritten: There’s a [paper](http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1906r0.pdf) aiming to change that in the core language. – Davis Herring Nov 03 '19 at 00:04
  • @DavisHerring Why do you say that assignment return value is `lvalue` and not `xvalue`(or `rvalue`). If it is `lvalue`, it should have identity and associated memory area. What would be identity of return value and where would be its memory? – UnSat Nov 03 '19 at 00:05
  • 1
    @UnSat Thinks of it this way: `category_cast(an_rvalue)` (pseudocode). It just treats the materialized object of the rvalue as an lvalue. – L. F. Nov 03 '19 at 00:08
  • 2
    @UnSat: …Its identity and storage are that of the temporary materialized to make the xvalue object expression for the (implicit) function call. But it’s a lot easier to say “It returns `std::string&`, so it’s an lvalue.”. – Davis Herring Nov 03 '19 at 00:14
  • 2
    @UnSat That's a simplified view on what value categories are. It might be a helpful analogy for some simple scenarios but don't treat it as a definition. – Lightness Races in Orbit Nov 03 '19 at 00:15
2

That rule only applies for objects of built-in type, like int.

It does not apply for classes.

The rule was probably deemed too restrictive for classes, whose operators can be overloaded to do various things … or perhaps deemed too restrictive all around by the time C++ came about, but it wouldn't have been alright to break old C code by relaxing the rule for built-ins too.

Regardless, the result of your addition is actually an lvalue, because that's what you get when you return an lvalue reference like std::string& from a function. That's despite the fact that both operands of the addition were rvalue expressions and temporary objects. It's perhaps a bit surprising here, and there's a proposal to do better at "propagating value category" in such cases, now that the language gives us the tools to do so.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    The result of addition (the `+` expression which is an `operator+` call) is an rvalue. The result of assignment is an lvalue. – aschepler Nov 03 '19 at 02:47
  • 1
    @aschepler Hmm [guess you're right](http://coliru.stacked-crooked.com/a/4b388e67f3f05d22). I never thought the rules for built-in ops would apply to the overloaded ones. Indeed, I'm sure there was a case recently where one such rule clearly did not. I smell inconsistency – Lightness Races in Orbit Nov 03 '19 at 16:48
  • It's not using the rule from a built-in operator. The result of adding two strings is an rvalue because the `operator+` function used returns a `std::string` (not a reference). – aschepler Nov 04 '19 at 02:13
  • @aschepler .... oh yeah. Why the hell was I thinking of compound assignment? I need a holiday. – Lightness Races in Orbit Nov 04 '19 at 10:10