5

We have:

vector<int> f(int);

vector<int> v;

This works:

f(x).swap(v);

This doesn't:

v.swap(f(x));

And why?

jason
  • 236,483
  • 35
  • 423
  • 525
yoyo
  • 1,113
  • 2
  • 17
  • 25

3 Answers3

7

swap() takes a non-const reference to a vector<int>. A non-const reference cannot bind to an rvalue (a temporary object). A call to a function that returns by value (like f) is an rvalue.

The reason that f(x).swap(v) works is because inside of std::vector<int>::swap, the temporary object returned by f(x) can use this to refer to itself. this is not an rvalue.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Why `non-const reference cannot bind to an rvalue `? – yoyo Nov 05 '10 at 06:02
  • 4
    @yoyo: That is discussed in [How come a non-const reference cannot bind to a temporary object?](http://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to-a-temporary-object) (especially sbi's answer) – James McNellis Nov 05 '10 at 06:05
  • I personally consider it a very awkward decision in a language where everything else is up to the programmer. I am slightly amused that `move` semantics and the associated "destructive" references will now allow somewhat similar. – Matthieu M. Nov 05 '10 at 07:37
  • @Matthieu: That decision was made in the very early history of C++, when there was no experience with `const` in C++. Maybe Stroustrup would decided otherwise today, and maybe he wouldn't. And if he had decided otherwise back then, maybe we would look at it the same way we look at the decision to make one-argument ctors implicit conversion operators and similar others: in hind side, it's largely seen as an error. – sbi Nov 07 '10 at 20:06
  • @sbi: I agree, it's always easier to criticize afterward, once you've gathered sufficient experience :) – Matthieu M. Nov 08 '10 at 07:23
  • @MatthieuM. "_in a language where everything else is up to the programmer_" Not really. But in this case, it is up to the programmer to introduce a named object. A rvalue of class type is just an unnamed object implicitly "declared" (and managed) by the compiler. OTOH, lvalues are under the control of the programmer, and it's the programmer responsibility to ensure that the lifetime of the lvalue is correct. There is a risk with allowing binding to rvalue: the risk that the programmer actually wanted to bind to a lvalue. That's why you should write `const int& r=*&f();` not `const int& r=f();` – curiousguy Dec 07 '11 at 08:54
1

You are allowed to call member functions on temporaries but in C++ they cannot be bound to non-const references.

For example:

int &x = 5; // illegal because temporary int(5) cannot be bound to non-const reference x
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • In this case, it is clear that you cannot bind `x` to the result of the evaluation of `5`, because the evaluation of `5` yields the value 5 of type `int`, so you would not have any object of type `int` to bind to. IOW, there is **no temporary** `int(5)` here. – curiousguy Dec 07 '11 at 08:27
  • @curiousguy: Same thing again? It doesn't create a temporary? Why does `int const & x = 5` work? – Nawaz Dec 07 '11 at 15:41
  • @curiousguy: How can you say that? How do you differentiate it from the situation when I write `int & x = 5`? – Nawaz Dec 07 '11 at 15:58
  • @curiousguy: I would also like to ask you what is the *type* of the temporary created here : `int const & x = 5`? – Nawaz Dec 07 '11 at 16:00
  • @Nawaz "_How do you differentiate_" I don't - the standard does. The rules for mutable and constant reference binding are different. This is not a nice theory you can deduce from axioms, it's a set of rules designed by B.S. and a committee. The rules says it's different, so it's different. You can explain why the rules were invented, you cannot reduce them to meta-rules. – curiousguy Dec 07 '11 at 16:05
  • @curiousguy: "*I don't - the standard does*". Then quote the standard where it says so. – Nawaz Dec 07 '11 at 16:09
  • @Nawaz "_what is the type of the temporary created here_" `const int` – curiousguy Dec 08 '11 at 10:41
  • @curiousguy: Alright. Btw, are you still searching for the reference in the spec? – Nawaz Dec 08 '11 at 10:59
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5686/discussion-between-curiousguy-and-nawaz) – curiousguy Dec 08 '11 at 12:27
1

Actually, (while James' answer is certainly right (and so is Prasoon's), there is some underlying problem to grasp.

When we reduce f(x) to its result y, and y.swap(v) (or v.swap(y), it doesn't matter in this case) to use generalized identifier names, it becomes

y.func(v)

Now, func() being a member function with one argument, it actually has two arguments: what's been passed in as v, and the implicit this pointer every non-static member function receives, here bound to y. Tossing encapsulation aside, every member function called as y.func(v) could be made a non-member function to be called as func(y,v). (And in fact, there actually are a non-member swap() functions. Also, each time you need to overload one of those binary operators that could be overloaded both as members or non-members, you have to make this decision.)

However, there are subtle differences between y.func(v) and func(y,v), because C++ treats the this argument, the argument that's passed by writing it before the . (the dot), different than the other arguments, and it does so in many ways.
As you have discovered, the this argument might be an rvalue (temporary) even for non-const member functions, while for the other arguments, a non-const reference prevents rvalues from being bound to the argument. Also, the this argument's run-time type might influence which function is called (for virtual members), while the other arguments' run-time type is irrelevant, because a function is chosen only depending on their compile-time type only. And implicit conversions are only ever applied to the explicit arguments of a member functions, but never to its implicit this argument. (That's why you can pass a string literal for a const std::string&, but cannot call std::string::size() on a string literal.)

So, to conclude, despite the fact that what's before the . ends up as an (implicit) function argument, it's actually treated very differently from the other function arguments.

Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • @yoyo: I can see that it's hard to understand, since my choice of variable names was poor. I Apologize. I've changed the names, hopefully it's easier to understand now. – sbi Nov 08 '10 at 08:12