Article Discussion
A Brief Introduction to Rvalue References
Summary: Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higher performance and more robust libraries.
64 posts on 5 pages.      
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: December 16, 2014 10:40 AM by
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 17, 2008 9:30 AM      
Since the creators of the language visit this board, could they please drop a few comments on the viability/usability of the move semantics on the programming language perspective level? methinks it's a bad move, because of all the problems of move semantics as we know them from std::auto_ptr...
Howard
Posts: 9 / Nickname: hinnant / Registered: December 16, 2007 7:49 AM
Re: A Brief Introduction to Rvalue References
March 17, 2008 10:56 AM      
> Howard wrote:
> > I was nervous about allowing statements such as:
> > int i = 0;
> > std::swap(i, 2);
>
> Wouldn't it just make a temporary, having the value 2, and
> swap i and the temporary?

Probably. I've never had the motivation to find out. I.e. I don't find swap(i, 2) a motivating use case.

> > The motivation for allowing containers to swap with
> > rvalues grew out of the desire to generalize the vector
> > "swap trick"
>
> Okay, like you wrote at:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n18
> 58.html#23.2%20-%20Sequences
>
> Personally I would prefer to have a dedicated
> shrink_to_fit function for this purpose. As proposed by
> Beman Dawes, LWG issue 755, std::vector and std:string
> lack explicit shrink-to-fit
> operations

> http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html
> #755

This looks likely to pass as well.

> But anyway, the new std::array container doesn't have
> std::swap overloads for rvalue references either. Are
> those left out because an array cannot shrink?

I suspect this is simply an oversight. We're still working feverishly on the draft.
Howard
Posts: 9 / Nickname: hinnant / Registered: December 16, 2007 7:49 AM
Re: A Brief Introduction to Rvalue References
March 17, 2008 11:27 AM      
> Since the creators of the language visit this board, could
> they please drop a few comments on the viability/usability
> of the move semantics on the programming language
> perspective level? methinks it's a bad move, because of
> all the problems of move semantics as we know them from
> std::auto_ptr...

I consider std::auto_ptr a pioneer in the area of move semantics. There is one very important detail it got wrong (and had little choice given the lack of language support in C++98): It moves from lvalues.

The move semantics framework in C++0X will not implicitly move from lvalues (with a couple of exceptions noted below). It will only move from rvalues. When you move from an rvalue, no other part of your program will notice because if you know you have a reference to an rvalue, then you also know that no other part of your program is referencing that rvalue.

Exception 1:

Sometimes it is advantageous for the programmer to be able to say: "treat this lvalue as an rvalue for this one operation". The programmer might want to do this when he knows (for example) that right after he "copies" from x, he is going to assign a new value to it, or destruct it anyway. So the old value of x is no longer valuable to the program logic (except as "copied" to the new location). A prime example is when vector reallocates its buffer. There is no need to copy values from the old buffer to the new; they can often be much more efficiently moved from the old buffer to the new buffer. To do so, the author of vector must explicitly specify the operation as a move since the values in the old buffer are lvalues.

Exception 2:

When copy elision is legal, but unable to be performed, an implicit move from an lvalue can be performed. Example:


A foo(bool b)
{
A a1, a2;
if (b)
return a2;
return a1;
}


In the above example, it is legal but exceedingly difficult for the compiler to perform Return Value Optimization. In C++0X the compiler will treat a1 and a2 as rvalues in the return statements. If A has a move constructor, it will be used. Else if A has a copy constructor it will be used. Else the program is ill-formed.

The result of these rules is that some programs which make heavy use of STL or other move-enabled libraries may simply start to execute much faster by simply recompiling in C++0X: with no changes needed, with no rvalue reference or move in sight, and no change in semantics. However in the interest of backwards compatibility, for the user's own classes to start moving, he will have to explicitly code move constructors and move assignment. When he does so, the framework is designed so that his clients will simply experience increased performance, and not a change in semantics.

Further background information regarding move semantics and auto_ptr can be found here:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#20.4.5%20-%20Class%20template%20auto_ptr

The most important points of this link are:

1. One should not move from lvalues using copy syntax. Other syntax for moving should be used instead. Otherwise generic code is likely to initiate a move when a copy was intended.

2. Even though it is unsafe to move from an lvalue with copy syntax, it is both safe and desirable to move from an rvalue with copy syntax. When an rvalue binds to a function, that function has a unique reference to the rvalue and thus can safely modify it.
Niels
Posts: 9 / Nickname: dekker / Registered: September 7, 2006 6:14 AM
Re: A Brief Introduction to Rvalue References
March 17, 2008 0:23 PM      
Howard wrote:
> I don't find swap(i, 2) a motivating use case.

In templated code, you might want to do something like swap(foo, CreateNewFoo()), for foo being either an STL container or a built-in type... Would that be a motivating use case to you?

>> Personally I would prefer to have a dedicated
>> shrink_to_fit function for this purpose. As
>> proposed by Beman Dawes, LWG issue 755 [...]
> This looks likely to pass as well.

I'm glad to hear so! I hope that the new Standard will specify that shrink_to_fit should be at least as effective as the swap trick...

>> But anyway, the new std::array container doesn't have
>> std::swap overloads for rvalue references either.
>
> I suspect this is simply an oversight.

So even without being shrinkable, you still think that std::array should have the same kinds of swap overloads as the other STL containers, right? It sounds like those overloads would become part of the STL container "concept"...
Howard
Posts: 9 / Nickname: hinnant / Registered: December 16, 2007 7:49 AM
Re: A Brief Introduction to Rvalue References
March 17, 2008 4:26 PM      
> Howard wrote:
> > I don't find swap(i, 2) a motivating use case.
>
> In templated code, you might want to do something
> like swap(foo, CreateNewFoo()), for
> foo being either an STL container or a
> built-in type... Would that be a motivating use case to
> you?

<shrug> Maybe. I've seen the motivation with wanting to swap with an rvalue container. But over the past decade I haven't run across someone complaining that the general swap doesn't accept rvalues, with the exception of use of proxy references. And in this latter case the solution seems to be to overload swap on your proxy reference.

I guess I'm a "squeaky wheel" kind of guy. I know I can't solve all the problems. So I try to tackle what I perceive to be the biggest problems first. How serious is this problem you're point out? You just would not believe the effort it takes to get a change through committee... (which is as it should be)

> >> But anyway, the new std::array container doesn't have
> >> std::swap overloads for rvalue references either.
> >
> > I suspect this is simply an oversight.
>
> So even without being shrinkable, you still think that
> std::array should have the same kinds of swap overloads as
> the other STL containers, right? It sounds like those
> overloads would become part of the STL container
> "concept"...

It's swap capability should probably be consistent with the other containers (whatever we decide that capability is). It would be awkward if one could swap against an rvalue vector but not an rvalue array. But I see a much larger difference when I sub in "rvalue scalar" into that consistency argument. C++ is very much a pragmatic language, as opposed to a pure theory language.
Niels
Posts: 9 / Nickname: dekker / Registered: September 7, 2006 6:14 AM
Re: A Brief Introduction to Rvalue References
March 18, 2008 11:44 AM      
>> In templated code, you might want to do something
>> like swap(foo, CreateNewFoo()), for foo
>> being either an STL container or a built-in type... Would that be
>> a motivating use case to you?
>
> <shrug> Maybe.

Okay... now suppose there's a third party class, ThirdPartySpace::Foo, having an accessible move constructor and move assignment operator. Unfortunately the third party didn't provide an overload to swap an rvalue of this class. Still the user might want to do so, within her own namespace:

using std::swap;
swap(foo, CreateNewFoo());

Within templated code, of course, so foo could be either an STL container or a ThirdPartySpace::Foo. Would that be a motivating use case? (Oops, another shrug?)

> I've seen the motivation with wanting to swap
> with an rvalue container.

Old C++03 already supports swapping with an rvalue container! Albeit by calling the member swap of a temporary, of course...

> But over the past decade I haven't run across
> someone complaining that the general swap doesn't
> accept rvalues

Good point... although the introduction of rvalue references might significantly increase the use of rvalues! :-)

> You just would not believe the effort it takes
> to get a change through committee...

Okay, I believe you, I'm sorry! Still I tend to think that it would be better to either add rvalue reference support to std::swap for all possible types, or just remove those std::swap overloads from the STL containers as well.

Anyway, your replies are very helpful to me, getting an understanding of what rvalue references are about, so thanks again!
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 19, 2008 5:10 AM      
> I consider std::auto_ptr a pioneer in the area of move
> semantics. There is one very important detail it got
> wrong (and had little choice given the lack of language
> support in C++98): It moves from lvalues.

I don't think the problem was the lack of rvalues. Every time I tried to use std::auto_ptr, I had problems. And even if the initial code was correct, after changing the code many problems with invalid ownerships and deletions of objects in the wrong places came up.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 19, 2008 5:28 AM      
> Exception 2:
>
> When copy elision is legal, but unable to be performed, an
> implicit move from an lvalue can be performed. Example:
>
>
> A foo(bool b)
> {
> A a1, a2;
> if (b)
> return a2;
> return a1;
> }
>

>
> In the above example, it is legal but exceedingly
> difficult for the compiler to perform Return Value
> Optimization.

I don't understand why the compiler can't perform return value optimization. In an optimizing build, the compiler could eliminate the two created instances and construct the result directly, whatever path is taken.

>
> The result of these rules is that some programs which make
> heavy use of STL or other move-enabled libraries may
> simply start to execute much faster by simply recompiling
> in C++0X: with no changes needed, with no rvalue
> reference or move in sight, and no change in semantics.
> However in the interest of backwards compatibility, for
> r the user's own classes to start moving, he will have to
> explicitly code move constructors and move assignment.
> When he does so, the framework is designed so that his
> s clients will simply experience increased performance,
> and not a change in semantics.

I have to disagree. Move semantics is a very big change. Consider the following example:


void bar() {
A a = foo();
A b = a; //move
a->doSomething(); //oops, a's contents are null.
}


> 1. One should not move from lvalues using copy syntax.
> Other syntax for moving should be used instead. Otherwise
> generic code is likely to initiate a move when a copy was
> intended.

Which means I would have to use the function 'move' every time I want to move data, instead of copying them.

This could be achieved easily by a move object; the compiler certainly did not need rvalues. You could do it like this:


template <class T> class move_t {
///bla bla
};

//move function returns a move object from the given reference
template <class T> move_t<T> move(T &p) {
return move_t<T>(p);
}

//use move
A b = move(a);


But I see a contradiction here: you say that "there is no need to modify code and the move constructor will be implicitely used to increase performance", and now you say that "the move function should be used, for safety reasons". Not very good, in my opinion.

> 2. Even though it is unsafe to move from an lvalue with
> copy syntax, it is both safe and desirable to move from an
> rvalue with copy syntax. When an rvalue binds to a
> function, that function has a unique reference to the
> rvalue and thus can safely modify it.

So in complex expressions of the form:


foo(b, bar(a, a.foo(b.bar(a)), a)


it's going to be a hell of a lot difficult to understand why the program crashes! move semantics makes destruction almost random.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 19, 2008 5:38 AM      
I've just read the stuff about unique_ptr...all I can say is 'whoa'.

In order to optimize STL 0.0000001 per cent, you just made this horrible mess of deleters, unique ptrs, rvalues, and the lot.

What's the point of all this?

C++ is fast enough as it is.

Due to lack of garbage collection, C++ is not used where stability and performance is important (for example, in web servers).

On the other hand, embedded software does not have a need so much sophisticated memory management techniques, simply because when resources are in shortage, static allocation is the preferred method of operation.

So what is left? desktop applications in C++...shared pointers are more than enough, and the extra ounce of performance gained by move semantics is insignificant for such applications.

Methinks the new move semantics in C++ are there just because you could do it, and for no other reason. Instead of introducing garbage collection and making all these move things, unique ptrs, auto ptrs, shared ptrs, deleters, reference traits etc a thing of the past, you insist on doing it the hard way.
Ion
Posts: 8 / Nickname: igaztanaga / Registered: January 3, 2006 9:57 PM
Re: A Brief Introduction to Rvalue References
March 19, 2008 10:07 AM      
> I've just read the stuff about unique_ptr...all I can say
> is 'whoa'.
>
> In order to optimize STL 0.0000001 per cent, you just made
> this horrible mess of deleters, unique ptrs, rvalues, and
> the lot.
>
> What's the point of all this?

Read it again. Move semantics offer a "huge" speed improvement. Just try to use std::vector<string> with or wihout move semantics and you will see what I am talking about.

> C++ is fast enough as it is.

No, not fast enough.

> Due to lack of garbage collection, C++ is not used where
> stability and performance is important (for example, in
> web servers).

Achilleas. In every article, your reply is "we need garbage collection". Garbage collection work is underway, but I don't think it's necessary at all.

> On the other hand, embedded software does not have a need
> so much sophisticated memory management techniques, simply
> because when resources are in shortage, static allocation
> is the preferred method of operation.

I disagree. Embedded software will welcome move semantics precisely because it avoids memory allocation for many common operations. unique_ptr will be welcome because it offers reasonable leaking defense at ease of use for containers of pointers built to take advantage of polymorphism.
Nemanja
Posts: 40 / Nickname: ntrif / Registered: June 30, 2004 1:10 AM
Re: A Brief Introduction to Rvalue References
March 19, 2008 10:43 AM      
> Garbage collection work is underway,
> but I don't think it's necessary at all.
>

IMHO, it is harmful. It would encourage people to stay away from value semantics and create more objects on the heap. On the contrary, we need rvalue references, not garbage collection. C++ is simply too low level language to afford GC.
Bjarne
Posts: 48 / Nickname: bjarne / Registered: October 17, 2003 3:32 AM
Re: A Brief Introduction to Rvalue References
March 20, 2008 9:28 AM      
> C++ is fast enough as it is.

Many people disagree. In particular, if you want to write simple programs using value semantics, the cost of copying can become a problem. Rvalue references - which after all is the source of this particular debate - were introduced to transparently eliminate redundant copies. When used right (meaning under the covers of a class needing value semantics) they simply eliminate waste - they are an optimization mechanism. The problem they address is beyond the scope of current and likely near-future optimizers.


> Due to lack of garbage collection, C++ is not used where
> ...

C++ will get GC. GC is not a panacea, though, and about as many complain about the possibility of the ("useless, complex, and redundant") GC as do complain about its current absence.

I would have liked to see (programmer controlled) GC in C++0x, but I'll have to wait for a TR. IMO *many* will benefit from GC and *many* will live happily without using it even after it becomes standard.


> Methinks the new move semantics in C++ are there just
> because you could do it, and for no other reason.

You happen to be wrong about that. Rvalue references (move semantics) addresses a well known and significant problem with one of the most popular programming styles.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 21, 2008 4:49 AM      
> Read it again. Move semantics offer a "huge" speed
> improvement. Just try to use std::vector<string> with or
> wihout move semantics and you will see what I am talking
> about.

std::vector<string> is slow. QVector<QString> is not. It's seems that's a matter of implementation.

>
> > C++ is fast enough as it is.
>
> No, not fast enough.

I have to disagree. There is nothing in the current C++ standard that prohibits from writing the fastest possible code. For example, if you want the fastest possible table of strings, you can write it in C++ as it is right now.

>
> > Due to lack of garbage collection, C++ is not used
> where
> > stability and performance is important (for example, in
> > web servers).
>
> Achilleas. In every article, your reply is "we need
> garbage collection". Garbage collection work is underway,
> but I don't think it's necessary at all.

Perhaps because you don't have to develop and maintain complex C++ applications.

I've spend the last 5 years in 90% debugging and 10% developing C++ software, and 70% of the bugs were due to manual memory management (wild pointers, memory blocks deleted multiple times, arrays out of bounds etc).

The lack of garbage collection in C++ has cost my company quite a lot.

>
> > On the other hand, embedded software does not have a
> need
> > so much sophisticated memory management techniques,
> simply
> > because when resources are in shortage, static
> allocation
> > is the preferred method of operation.
>
> I disagree. Embedded software will welcome move semantics
> precisely because it avoids memory allocation for many
> common operations. unique_ptr will be welcome because it
> offers reasonable leaking defense at ease of use for
> containers of pointers built to take advantage of
> polymorphism.

I do not question the need for more efficient abstractions in the case of embedded systems. What I do question is that the language needed changes in order to have these more efficient abstractions.

The new features add significantly more complexity to an already-out-of-control complex language. If, in a few years, there is an outcry against move semantics because people can't get their programs to behave predictably (because data move around in a chaotic fashion), don't come back and say you haven't been warned.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 21, 2008 4:58 AM      
> > Garbage collection work is underway,
> > but I don't think it's necessary at all.
> >
>
> IMHO, it is harmful. It would encourage people to stay
> away from value semantics and create more objects on the
> heap.

No, it's not. I don't understand how you can claim that value semantics is all that's needed to write complex systems. In every sufficiently complex (i.e. non-trivial) program I have worked with (which was not very big or complex compared to really big projects), I could not have used value semantics for all the requirements.

> On the contrary, we need rvalue references, not
> garbage collection. C++ is simply too low level language
> to afford GC.

But GC works beautifully with C++! having studied GC systems extensively, I see no technical obstacle in having garbage collection in C++.

I have to disagree with stubbornness against move semantics. The average coder will simply choke on it. Complex expressions like a.b(a, b.c(a)) will create intractable bugs where only the most experienced programmers will be able to understand.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
March 21, 2008 5:24 AM      
> > C++ is fast enough as it is.
>
> Many people disagree. In particular, if you want to write
> simple programs using value semantics, the cost of copying
> can become a problem.

If your program is so simple that it can only have value semantics, then it can be written in such a way that no copying is required. Copying is costly mainly when allocations are involved. If allocations are involved, then you don't have only value semantics, you also have reference semantics, albeit hidden.

On the other hand, if your program is so sensitive in performance that it needs that extra ounce of efficiency gained by rvalues, then it's not that simple, is it? in that case, you will have already coded your algorithms in such a way that redundant copying would not exist.

Rvalue references - which after all
> is the source of this particular debate - were introduced
> to transparently eliminate redundant copies. When used
> right (meaning under the covers of a class needing value
> semantics) they simply eliminate waste - they are an
> optimization mechanism. The problem they address is beyond
> the scope of current and likely near-future optimizers.

But redundant copies exist because objects are allocated in scoped memory (i.e. the stack). Instead of fixing the source of the problem, you provided a patch, whereas fixing the problem will have solved a much greater spectrum of problems without introducing new ones.

>
>
> > Due to lack of garbage collection, C++ is not used
> where
> > ...
>
> C++ will get GC. GC is not a panacea, though, and about as
> many complain about the possibility of the ("useless,
> complex, and redundant") GC as do complain about its
> current absence.

>
> I would have liked to see (programmer controlled) GC in
> C++0x, but I'll have to wait for a TR. IMO *many* will
> benefit from GC and *many* will live happily without using
> it even after it becomes standard.

Why should they complain if it is optional? I do not claim that all C++ objects must be allocated on the heap. Stack allocation has important efficiency benefits, as well as RAII (very useful).

>
>
> > Methinks the new move semantics in C++ are there just
> > because you could do it, and for no other reason.
>
> You happen to be wrong about that. Rvalue references (move
> semantics) addresses a well known and significant problem
> with one of the most popular programming styles.

RValue references addresses a well known and significant problem which would not have existed if other solutions were preferred in the first place. By insisting on manual memory management, the need to avoid copying arises, which in turn makes rvalues seem a good solution.

Let me put it in this way: what needs to be moved with move semantics can be allocated on the heap using garbage collection and therefore all issues of language complexity and run-time efficiency, etc could be avoided.

The inclusion of smart pointers in the standard library points to the need of automating the task of memory management.
64 posts on 5 pages.