0
 1 class Foo{
 2 
 3 };
 4 
 5 Foo func1(){
 6   Foo f;
 7   return f;
 8 }
 9 
10 int func2(){
11   int a = 2;
12   return a;
13 }
14 
15 int main(int argc, char** argv){
16   Foo var1;
17   func1() = var1; //OK
18   func2() = 1; //error: expression is not assignable
19   return 0;
20 }

What is the fundamental reason that assignment to the return value that is of built-in type is not allowed but assignment to a return value that is user-defined type is allowed? How is the memory managed that allows one but not the other?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
parsimons
  • 11
  • 1
  • 1
    What do you think `func1() = var1;` this means? – Ed Heal Oct 31 '16 at 04:43
  • I understand that it calls the assignment operator of Foo class, my question is what is the fundamental difference between the built-in types and user-defined types in this matter? Foo& operator=(Foo&); – parsimons Oct 31 '16 at 04:54
  • .. and then what? – Ed Heal Oct 31 '16 at 04:57
  • The copy assignment operator creates a copy of the source and makes a member-wise copy of each data member. I understand why this one works. Why doesn't it work with the built-in type? – parsimons Oct 31 '16 at 05:05
  • It then throws away the result. ie. effectively does nothing – Ed Heal Oct 31 '16 at 05:08
  • Can you explain the mechanism? The two functions are similar. The only difference is in the return type. The closest thing I have found is the third answer by Jason Coco http://stackoverflow.com/a/275226/7093486 Is this accurate? (stack vs heap?) – parsimons Oct 31 '16 at 05:13

1 Answers1

1

Take a look at this article about value categories.

Value categories

Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category. [...]

prvalue

The following expressions are prvalue expressions:

[...]

  • function call or an overloaded operator expression of non-reference return type, such as str.substr(1, 2)

OK, so we know that expressions func1() and func2() are prvalues.

[...]

rvalue

An rvalue expression is either prvalue or xvalue.

Now we know that prvalues are rvalues.

Properties:

[...]

An rvalue can't be used as the left-hand operand of the built-in assignment or compound assignment operators.

[...]

Here is the important part. Note that the rule only excludes built-in assignment. Copy assignment operator of a class type is still allowed.

So, the difference between built-in operators, and operators of class types is that they are treated differently by the rules of the language.


Now, you might be wondering, why is assignment not allowed for built-in types? Well, there is no use case for it. The assignment has no side-effects (besides changing the value of the left hand operand) and the return value is discarded. If you write it, you've most likely made a mistake. Disallowing it is helpful to the programmer.

Now, you might be wondering, isn't assigning to an rvalue class type also useless? Well, it might not be! Class types can have user defined assignment operators that have side-effects. And class types can have destructors that have side-effects depending on the previously performed assignment. I cannot guarantee that this was the reasoning that Bjarne or the committee used when they specified the language, but those are my thoughts on the matter.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    Perhaps the automatically synthesized assignment operator should be `operator=(const T&) &;` in order that it would apply only to lvalues (note the trailing `&` there). This is more intuitive and perhaps in accordance with the OP's intuition. Why would you assign into a temporary? And a developer could then write `operator(const T&) && = default;` if they want to 'force' the compiler to allow assignment into rvalues. – Aaron McDaid Oct 31 '16 at 10:42
  • 1
    @AaronMcDaid well, assignment has existed before c++11 but ref-qualifiers haven't, so that couldn't have been how the implicit copy assignment was originally specified. Now that ref-qualifiers exist, such change would be possible and its adoption will depend on whether the committee work groups consider such change worth their time - assuming such change is officially proposed in the first place. – eerorika Oct 31 '16 at 10:53
  • 1
    On drawback I can see is that you might want to use a template that does the assignment magic for types with custom overload, and silently optimize away the assignment for types with implicit operator. Your proposed change would be backwards incompatible with such template. – eerorika Oct 31 '16 at 10:53