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
Bjarne
Posts: 48 / Nickname: bjarne / Registered: October 17, 2003 3:32 AM
Re: A Brief Introduction to Rvalue References
March 21, 2008 6:17 PM      
> 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. ...
>
> 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? ...
>
> But redundant copies exist because objects are allocated
> in scoped memory (i.e. the stack). ...

Telling people to to write (and rewrite) their code in the style you would have chosen is not an option.

If nothing else, you may never have encountered the problems they set out to solve.


> > 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?

But they do. Loudly. And they are not all inexperienced or stupid.


> RValue references addresses a well known and significant
> problem which would not have existed if other solutions
> were preferred in the first place.

But (many) people
(1) don't agree and
(2) do not prefer those other solutions
(3) have lots of code written the way they like it
Mathias
Posts: 1 / Nickname: loufoque / Registered: May 15, 2008 1:35 AM
Re: A Brief Introduction to Rvalue References
May 15, 2008 6:48 AM      
It seems there are a few errors(?) in that article.

First, this piece of code:
    clone_ptr& operator=(const clone_ptr& p)
{
if (this != &p)
{
delete ptr;
ptr = p.ptr ? p.ptr->clone() : 0;
}
return *this;
}


If p.ptr->clone() throws, ptr is left in a dangerous state, which will likely produce a segfault when the destructor is called.
Two solutions:
- clone before deleting (better, since you keep the old value)
- catch the exception and set ptr to 0 (objects ends up empty)

Second, this piece of code:
    clone_ptr& operator=(clone_ptr&& p)
{
std::swap(ptr, p.ptr);
return *this;
}

Transfering the old pointed-to value to the rvalue (which might actually not really be one) doesn't seem like a good idea, since you can't really tell when it's gonna be freed.
It seems more reasonable to do
delete ptr;
ptr = p.ptr
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
May 27, 2008 4:17 AM      
> > 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. ...
> >
> > 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?
> ...
> >
> > But redundant copies exist because objects are
> allocated
> > in scoped memory (i.e. the stack). ...
>
> Telling people to to write (and rewrite) their code in the
> style you would have chosen is not an option.

I would not like to make this a political issue. Sticking to the technological issues, my arguments are:

1) GC makes it easier to write complex programs.
2) GC makes it faster to write complex programs.
3) if you take all factors into consideration, the addition of GC in C++ would benefit the language far more than rvalues, and it would make rvalues redundant. In other words, rvalues is there to solve a memory management issue, whereas a GC solves that issue and all the other related issues (no shared ptrs etc).

>
> If nothing else, you may never have encountered the
> problems they set out to solve.
>

I don't think programs are very different, in essence. For example, we all have encountered the problem of sharing data between collections and other parts of the program. Common problems like are solved nicely by GC.

If you have any examples of the problems that they set out to solve that are so different that rvalues is a better solution than garbage collection, then please feel free to share them with us.

> > > 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?
>
> But they do. Loudly. And they are not all inexperienced or
> stupid.

But you did not answer the question: why do they complain? the only reason I can see is stubbornness.

>
>
> > RValue references addresses a well known and
> significant
> > problem which would not have existed if other solutions
> > were preferred in the first place.
>
> But (many) people
> (1) don't agree and
> (2) do not prefer those other solutions
> (3) have lots of code written the way they like it

Arguments, please:

1) Why they don't agree?
2) what is the benefit of the other solutions?
3) why can't they accept the fact that their code will run just as before, since the GC will be optional?
master
Posts: 1 / Nickname: masterfish / Registered: June 8, 2008 5:39 PM
Re: A Brief Introduction to Rvalue References
June 8, 2008 10:54 PM      
I think maybe 2 kind of cppers would not like GC
[1].those work directly with hardware,and
[2].those hate writting code just like java.
Jan
Posts: 4 / Nickname: janbessai / Registered: September 24, 2008 3:10 AM
Re: A Brief Introduction to Rvalue References
September 24, 2008 9:22 AM      
To distract from the controversial point of GC and to get back to topic:

If there is something I miss in C++, it is a simple way to write getters without unnecessary performance overhead.

The current state in code:

Method 1:

class Foo {
private:
Bar member;
public:
...
Bar get_modified_member() const {
Bar result = member;
result.modify();
return result;
}
};

This has the overhead of copying the result variable (RVO is not to be considered here, as it is no language feature and too compiler dependent to really rely on).

Method 2:

class Foo {
...
Bar & get_modified_member(Bar & result) const {
result = member;
result.modify();
return result;
}
};

void some_calling_function {
Foo foo;
...
Bar bar;
foo.get_modified_member(bar);
}

This is rather ugly to read (+needs extra documentation) and has the overhead of creating a local instance of Bar, which can be dangerous or even impossible if Bar has a side effected default constructor or if Bar is a template typename and is unknown to have a default constructor at all.

Method 3:

class Foo {
...
Bar * get_modified_member() const {
Bar * result = 0;
result = new Bar(member);
return result;
}
};

This is rather efficient, but needs extra considerations based on type details of Bar, if Bar has no copy constructor. Furthermore it brings in all problems of heap(/store) allocated memory management. This method could be improved with smart pointers, but that still needs extra work to avoid cyclic referencing and whatever other pitfalls the chosen smartpointer implementation has. The amount of extra documentation needed is in each case tremendous.


Do I get it correctly now, that C++0x will add
Method 4:

class Foo {
...
Bar get_modified_member() const {
result = member;
result.modify();
return std::move(result);
}
};

This will be efficient. It requires Bar to have a movement constructor.

If so (to be meant asking, not criticizing):
1. What will be returned if std::move(result) throws for some reason?
2. Is there any way to make sure or at least check, that Bar has a movement constructor (maybe using concepts?)? If so, how would that way look? Would it add difficult extra code?
3. Have you considered how to explain the std::move command and rvalue references to novices in an easy way? C++ is complex, but there must be somewhere to start and I think simple standard getter methods should remain a fully understandable part for beginners.

Besides I'd like to add two different ways of approaching the result value problem, which might be a consideration, too:
Method 5 (inspired by Delphi):

class Foo {
...
Bar get_modified_member() const {
result = member;
result.modify();
}
};

This method implies that each function gets an implicitly existing value to store the function results in. It is quite similar to early NRVO approaches. For flexibility and naming reasons it could by done by a new language keyword, too:
Method 5 (modified):

class Foo {
...
Bar get_modified_member() const {
resulting Bar result = member;
result.modify();
return result;
}
};

The resulting keyword could have the compiler explicitly turn on RVO for one local variable per function and thereby make RVO a reliable language feature.

Last (and least) an ugly workaround approach inspired by the way Java can instantiate arrays of generic types without knowing if a default constructor exists (java.lang.reflect.Array.newInstance(...))
Method 6:

class Foo {
...
Bar & get_modified_member(Bar & result) const {
result(member);
result.modify();
return result;
}
};

void some_calling_function {
Foo foo;
...
Bar bar = std::uninitialized_object<Bar>();
foo.get_modified_member(bar);
}


Method 5 (modified) would be my favorite addition to C++, as it is simple to use, compilers with RVO don't need many changes to implement it and it is most easy to explain.

Thanks for reading and maybe for answering :)!
rgamarra79
Posts: 7 / Nickname: rgamarra79 / Registered: September 26, 2008 3:00 AM
Re: A Brief Introduction to Rvalue References
September 26, 2008 9:35 AM      
Hi all,

I was trying to get familiarity with the concept of Rvalue references. While reading this article, the definition of forward has something I can't understand: the need for identity<T>::type:


template <class T>
T&& forward(typename identity<T>::type&& a)
{
return a;
}


Why can't it be done simply as the following?


template <class T>
T&& forward(T&& a)
{
return a;
}


I'm sure I'm missing something, but I can't figure it out.

In http://www.pdc.kth.se/training/Talks/C++/boost/libs/mpl/doc/index.html it can be read


// on the face of it, not very useful
template< typename T >
struct identity
{
typedef T type;
}


I also took a look at http://www.boost.org/doc/libs/1_36_0/libs/mpl/doc/refmanual/identity.html.

On the other hand


template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
return a;
}


does the its thing the other way round: using some MPL techniques for the return value and not for the argument's type.

Thanks a lot in advance.

Rodolfo.
Jan
Posts: 4 / Nickname: janbessai / Registered: September 24, 2008 3:10 AM
Re: A Brief Introduction to Rvalue References
September 26, 2008 2:55 PM      
C++0x should support template typedefs anyway. So you should be perfectly right as far as I can see it.
rgamarra79
Posts: 7 / Nickname: rgamarra79 / Registered: September 26, 2008 3:00 AM
Re: A Brief Introduction to Rvalue References
September 27, 2008 3:41 PM      
Thanks Jan.

I wasn't thinking about template typedefs, but it's good to have them in consideration. Indeed the technique used (identity struct) is the usual workaround to the lack of them (http://www.gotw.ca/gotw/079.htm).

I just could not see the need for that identity template struct, as its inner "typedef T type" would be equivalent to T.

So I asked myself: Why not just use T&& in forward's arg list (just like in the case of move)? And could not find a good reason.

I actually tried some examples (with regular references) and the the function could be defined (overloaded) both ways simultaneously. It seems that using identity makes it harder for the compiler to resolve the proper function: may identity be used to enforce typename specialization when calling the template function?

Below the code I tried. Thanks. Rodolfo.


#include<iostream>
// Identity struct.
template<typename T>
struct identity {
typedef T type;
};
//--
// Overloaded funcs.
// Both ways
template<typename T>
int foo1(T& t){
return 0;
}
template <typename T>
int foo1(typename identity<T>::type& t){
return 1;
}
// Only directly
template<typename T>
int foo2(T& t){
return 2;
}
// Only with identity.
template<typename T>
int foo3(typename identity<T>::type& t){
return 3;
}
// Main.
int main(){
int a = 0;
std::cout << foo1(a) << std::endl;
// error: call of overloaded ‘foo1(int&)’ is ambiguous
// std::cout << foo1<int>(a) << std::endl;
std::cout << foo2(a) << std::endl;
std::cout << foo2<int>(a) << std::endl;
// error: no matching function for call to ‘foo3(int&)’
// std::cout << foo3(a) << std::endl;
std::cout << foo3<int>(a) << std::endl;
return 0;
}
Jan
Posts: 4 / Nickname: janbessai / Registered: September 24, 2008 3:10 AM
Re: A Brief Introduction to Rvalue References
October 1, 2008 11:22 AM      
Hi Rodolfo,
I think foo3(a) does not work for a reason. Consider this:

include <iostream>

template<typename T>
struct identity {
typedef T type;
};

template <typename T>
struct identity_rek {
typedef typename T::type type;
};

template<typename T>
int foo3(typename identity<T>::type& t){
return 3;
}

template <typename T>
int foo4(typename identity_rek<T>::type& t) {
return 4;
}


int main(){
int a = 0;
// "simple" case
std::cout << foo3<identity_rek<identity<int> >::type>(a) << std::endl;
// "evil" case
std::cout << foo3<identity_rek<identity_rek<identity_rek<identity<int> > > >::type>(a) << std::endl;
// compiler riddle
std::cout << foo3(a) << std::endl;
// "more evil" case
std::cout << foo4<identity<identity_rek<identity<int> > >::type>(a) << std::endl;
// "more evil" riddle
std::cout << foo4(a) << std::endl;
return 0;
}


The compiler riddles have an infinite amount of solutions (making the result indeterministic) and could be constructed even worse. I guess algorithms to solve them would have to be heuristic. Maybe some solutions can even end up in an infinite recursion.
To have the compiler find out a right type to fill the template with (only by knowing that a is int) therefore is almost impossible.
Greetings and sorry for the brain damaging example,
Jan

PS.: Consider that all types "a" can be casted to would be valid solutions, too - adding infinite possible fillins and ambiguities again.
rgamarra79
Posts: 7 / Nickname: rgamarra79 / Registered: September 26, 2008 3:00 AM
Re: A Brief Introduction to Rvalue References
October 1, 2008 4:35 PM      
Jan,

Thanks for your reply. Indeed, using the function template without specialization delegates a lot of work to the compiler in order to deduce the intended substitution.

Your example and comments make clear how contrived the situation can be.

Nevertheless, with my example maybe I changed the focus of my original question: What's the reason behind the usage of identity? So far, it seems to me that it only serves as a way to force the programmer to specialize his invocations of forward.

Thanks a lot.

Rodolfo.
Jan
Posts: 4 / Nickname: janbessai / Registered: September 24, 2008 3:10 AM
Re: A Brief Introduction to Rvalue References
October 2, 2008 1:59 PM      
Hi,
it's pure speculation, but maybe the example was created using an incomplete compiler, which was not yet able to handle the
template <typename T>
void foo(T && bar) { ... } way correctly.
I cannot imagine any other reason (despite habitual programming style). Specially as the identify-version is much harder to read and to understand.

Jan.
Justin
Posts: 1 / Nickname: jgottsch / Registered: December 21, 2004 7:20 PM
Re: A Brief Introduction to Rvalue References
October 29, 2008 11:04 AM      
Hi,

I personally have found rvalue references to be critically important for my research in software transactional memory.

Here's a link to a paper we recently wrote that uses rvalue references for STM. It is invaluable for making it work correctly.

http://eces.colorado.edu/~gottschl/pubs/icooolps08-exception.pdf

Best Regards,
Justin
Marc
Posts: 1 / Nickname: marc77 / Registered: December 31, 2009 6:14 AM
Re: A Brief Introduction to Rvalue References
December 31, 2009 0:33 PM      
I like rvalues because they offer an interesting way to optimize programs. But I think the standard could have been made better.

First, in C you have restrict pointer. I believe C++0x should have tried to make rvalue references restricted.

Second, how to make a better "move" and guarantee rvalue references are restricted? Simple with a new rule for the destructor:
When the explicit destructor is called on a lvalue, the lvalue is destroyed but a copy is returned as a rvalue.

Now, if you don't use the returned rvalue, the class destructor is called on it like for any other rvalue.

Finally, I hope compilers will be able to automatically move a value when it is used for the last time or it's rvalue reference is used for the last time. This way, it would be possible to never use move and still receive most of the benefit from it.

I would like to have comments, so I would know at least 1 person has read it(I know it is an old topic).

Have a happy new year.
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: A Brief Introduction to Rvalue References
October 12, 2010 4:24 AM      
Hey, I was right after all: c++ move semantics created a problem for the STL library:

http://cpp-next.com/archive/2010/10/implicit-move-must-go/

In the above article, it is described how the move constructor chosen automatically over the copy constructor created a problem for a type that wasn't move-aware, i.e. the type assumed exclusive ownership of resources.

I still think move semantics is a very bad idea. I expect that there are going to be lots of problems in the future, exactly as the one described in the article above.
Gaetano
Posts: 1 / Nickname: kalman / Registered: November 1, 2010 7:22 AM
Re: A Brief Introduction to Rvalue References
November 1, 2010 0:27 PM      
> I would have no objections to this change. Another
> acceptable possibility is:
>
> clone_ptr& operator=(const clone_ptr& p)
> {
> if (this != &p)
> {
> delete ptr;
> ptr = 0;
> ptr = p.ptr ? p.ptr->clone() : 0;
> }
> return *this;
> }

For my taste this is still not acceptable, the code above still destroy the old class instance if the clone throws. Better the previous suggested change.
64 posts on 5 pages.