[rant] C++0x is ...so simple.
axilmar

C++0x is ...so simple. I mean, what are 30* pages of explanation between friends? almost nothing!!!

[/irony]

*at 1280x1024

kazzmir

Thats a pretty interesting article, thanks for pointing it out!

Matthew Leverton

It was only three pages. :-/

{"name":"599214","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/4\/a4149249b2f1d2e530ce0b4c56db8166.png","w":1200,"h":1920,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/a\/4\/a4149249b2f1d2e530ce0b4c56db8166"}599214

ixilom

Only one page here, just needed to scroll a bit.

Goalie Ca

Actually, that was just covering rvalues and associated topics. I'm still trying to wrap my head around it and how to properly write code that will use it. This will be probably huge in my scientific computing for performance.

Martin Kalbfuß

Is the font only small in my webbrowser? My eyes hurt. When I see such pages.

Matthew Leverton

Why have you started using excessive periods (stops)?

X-G
Goalie Ca said:

I'm still trying to wrap my head around it and how to properly write code that will use it

Aside from lambdas, rvalue references are probably the feature I am most excited about. For something so simple, it provides so much elegance for the very crucial features of move semantics and perfect forwarding.

anonymous

The downside seems to be that you might need lots of overloads for things. For example, std::string::operator+ has a whole lot of new overloads for rvalue references.

What would be interesting is to figure out how to get your objects prematurely destroyed by moving as people fear (find out how the move semantics of rvalue references is similar and dissimilar to std::auto_ptr). Below I have two ways (but not that impressive since you explicitly ask for it):

#SelectExpand
1#include <iostream> 2 3class X 4{ 5 int* p; 6public: 7 X(): p(0) { std::cout << "X()\n"; } 8 X(int n): p(new int(n)) { std::cout << "X(int)\n"; } 9 X(const X&) = delete; 10 X(X&& x): p(x.p) { x.p = 0; std::cout << "X(X&& x)\n"; } 11 X& operator=(const X&) = delete; 12 X& operator= (X&& other) 13 { 14 if (this != &other) { 15 delete p; 16 p = other.p; 17 other.p = 0; 18 } 19 std::cout << "operator=(X&&)\n"; 20 return *this; 21 } 22 ~X() {delete p; std::cout << "~X()\n"; } 23 void print() const 24 { 25 if (p) { 26 std::cout << *p << '\n'; 27 } 28 else { 29 std::cout << "NULL\n"; 30 } 31 } 32}; 33 34void foo(X&& x) 35{ 36 std::cout << "foo\n"; 37 x.print(); 38 X y(std::move(x)); 39 y.print(); 40} 41 42int main() 43{ 44 X a(10), b(20); 45 a.print(); 46 b.print(); 47 //a = b; //doesn't compile X::operator=(const X&) deleted 48 a = std::move(b); 49 a.print(); 50 b.print(); 51 foo(a); //works, but doesn't move a 52 a.print(); 53 foo(std::move(a)); //compiles, but still doesn't move a 54 a.print(); 55 foo(X(30)); 56}

Matthew Leverton

Why don't they name it C++&&<<=*?

X-G
anonymous said:

The downside seems to be that you might need lots of overloads for things.

Not need; Rvalue&& overloads are strictly optional. Going without them only means you fail to take advantage of move-semantics -- something you couldn't do in plain C++ anyway, and your code will work just fine without any additional overloads. It'll just do more copies than is necessary.

Quote:

but not that impressive since you explicitly ask for it

I don't think it counts if you explicitly shoot yourself in the foot by attempting to move an object you intend to keep using...

anonymous
X-G said:

I don't think it counts if you explicitly shoot yourself in the foot by attempting to move an object you intend to keep using...

There was a similar thread where someone argued that even if you need to be explicit about it, it just makes it too easy for junior programmers to destroy stuff senior programmers want to keep using. I suppose the argument goes that those juniors won't bother to read / understand the linked article (or similar) but know that rvalue references and std::move are good for performance...

Goalie Ca

C++ is a language I absolutely hate because it takes a demi-god to really understand it. You can become quite dangerous quite quickly but there is sooo much behind the scenes that you need to understand and incorporate in all levels of design. In this, C++ fails proper abstraction.

X-G

Personally, I don't trust language abstractions as a means of protecting someone from his own ineptitude. If anything, I think it makes it harder to spot them and weed them out or at least give them proper guidance; and at any rate, an inept person will screw up regardless of how much the language tries to shield him from it. That is not the purpose of language abstractions.

axilmar
anonymous said:

There was a similar thread where someone argued that even if you need to be explicit about it, it just makes it too easy for junior programmers to destroy stuff senior programmers want to keep using. I suppose the argument goes that those juniors won't bother to read / understand the linked article (or similar) but know that rvalue references and std::move are good for performance...

The Academia leans heavily towards non-destructive updates - Stroustrup and Co. think the opposite. I expect some big disasters from move semantics, since data will mysteriously vanish...the only reason move semantics exist because the C++ people hate garbage collection with a passion (and therefore manual memory management forces them not to return pointers to objects but wrappers that copy objects - and now that they move objects). If they really cared about performance, they would return pointers - or making everything implicitly shared, ala QString.

anonymous
axilmar said:

I expect some big disasters from move semantics, since data will mysteriously vanish

But it vanishes mysteriously only from temporaries as they go out of scope?

Ok, perhaps they should have followed their example of reinterpret_cast<T> (it's so ugly and long to keep people from using it), and named std::move std::move_it_so_I_might_never_see_my_data_again instead.

Quote:

If they really cared about performance, they would return pointers

Which will just trade one kind of potential errors for another? For one thing, you can't return pointers to locals (optimizing returning by value seems to be one of the points of move semantics - doesn't it transparently move the return value out of the function, as long as a move constructor/assignment exists?). So there will be lots more of dynamic allocation?

Tobias Dammers
X-G said:

Personally, I don't trust language abstractions as a means of protecting someone from his own ineptitude. If anything, I think it makes it harder to spot them and weed them out or at least give them proper guidance; and at any rate, an inept person will screw up regardless of how much the language tries to shield him from it. That is not the purpose of language abstractions.

A better reason for language abstractions is to reduce boilerplate code and signal the intention of a piece of code rather than the low-level implementation. This is always a balancing act of course.

bamccaig

I think rvalue references are a great idea. :) I'm only half way through the part 2 post now and I still have to go back and read part 1... I'm excited to see what other features to expect and what they'll look like (I may have already read about them, but I don't recall).

amarillion

I'm definitely going to use lambda's, that's a really useful addition and I can think of a few places in my games where they might apply.
auto is a no-brainer. Soon, nobody will remember how to live without.
I'll probably only use rvalue references through the standard library.

C++ is continuing to be the smorgasbord of programming languages. Just pick what you like :)

anonymous

auto is a no-brainer. Soon, nobody will remember how to live without.

:)

I made a wrapper for <algorithms> and found that some functions return an iterator or a const_iterator depending on the constness of the range. To avoid writing the same functions twice I came up with this:

#SelectExpand
1 namespace detail 2 { 3 template <class Range> 4 struct Iterator 5 { 6 typedef typename Range::iterator type; 7 }; 8 9 template <class Range> 10 struct Iterator<const Range> 11 { 12 typedef typename Range::const_iterator type; 13 }; 14 } 15 16 //usage: 17 template <class Range, class Type> 18 typename detail::Iterator<Range>::type find(Range& range, const Type& value);

Now, with auto there's no trickery needed:

template <class Range, class Type>
auto find(Range& range, const Type& value) -> decltype(range.begin());

//or ... -> decltype(std::find(range.begin(), range.end(), value))

rvalue references seem to require lots of overloads to use it actively: as I understand you can only take advantage of move semantics (std::move) safely when a) the functions leaves everything in a good state (e.g swap), b) std::move is used on a rvalue reference parameter if there is an overload that takes that parameter by const lvalue reference (so that the moving version is only used with the temporaries).

X-G

Of course it's possible to screw up, and it always will be, but I see nothing more difficult about using std::move properly than, say, designing copy semantics properly or even plain using pointers properly. Sure you can trip up, and you probably will, but no one is asking you to be perfect. The opportunities opened by move semantics, not just for performance but for finally being able to have proper noncopyable-but-movable objects (a more common usecase than one might think), far outweigh any initial difficulty encountered.

That wrapper looks similar to some stuff we have at work. Best stuff since sliced bread.

axilmar
anonymous said:

But it vanishes mysteriously only from temporaries as they go out of scope?

Temporaries may be passed to functions that keep pointers to them.

Quote:

Which will just trade one kind of potential errors for another? For one thing, you can't return pointers to locals (optimizing returning by value seems to be one of the points of move semantics - doesn't it transparently move the return value out of the function, as long as a move constructor/assignment exists?). So there will be lots more of dynamic allocation?

Which error are you talking about? I don't mean returning a pointer to a local, I mean allocating something on the heap and returning a pointer to it. Move semantics would not be required if functions returned pointers to heap data - but then C++ would need garbage collection, which is something the C++ committee does not want.

I wonder why they did not use implicit sharing though. Swap for vectors with implicit sharing is a simple pointer swap, for example. Any data structure that has value semantics but it has heap data internally is best treated with implicit sharing.

Goalie Ca

Objects being constructed and destructed inside STL have "mysteriously" crashed many people's code. C++ fails to actually encapsulate the inner workings in far too many cases. The language is a mess but until momentum shifts to something more sane then I guess I am stuck.

I absolutely love the idea of bytecode and VMs. I can write the numerical part of my code in a functional language, the gui in an object oriented one, and do scripting via some interactive one all without writing language bindings.

anonymous
axilmar said:

Temporaries may be passed to functions that keep pointers to them.

Example, please. How do I take a pointer to a temporary and manage to pass it to a function (as a rvalue reference) at the same time? In addition, would you really take the address of a temporary?

Quote:

I wonder why they did not use implicit sharing though. Swap for vectors with implicit sharing is a simple pointer swap, for example. Any data structure that has value semantics but it has heap data internally is best treated with implicit sharing.

I don't entirely understand. Do you suggest that implementing copy-on-write (?) for classes is simpler? Could you show an example of how you'd implement a swap for a vector (and do rvalue references have only to do with swapping)?

Quote:

Move semantics would not be required if functions returned pointers to heap data - but then C++ would need garbage collection, which is something the C++ committee does not want.

I have a feeling that a non-optional GC would just turn C++ into a completely different language (why not program in a GC language in the first place?) and optionally not using it would be out of the question if the standard way of doing things would be what you describe.

ImLeftFooted
X-G said:

Personally, I don't trust language abstractions as a means of protecting someone from his own ineptitude. If anything, I think it makes it harder to spot them and weed them out or at least give them proper guidance; and at any rate, an inept person will screw up regardless of how much the language tries to shield him from it. That is not the purpose of language abstractions.

Hear hear!

There is too much language-based oppression out there "for your own good."

bamccaig

I'm currently attempting to build the current mainline of GCC (from the SVN repository) in Linux so I can experiment with some of the features... :D

** EDIT **

The tests (make [options] -k check) did not go well. :o Bad Things(TM) apparently happened... :-X syslogd wrote apocalyptic messages to my terminal indicating that a reboot was necessary... :-/ Seemed to be CPU temperature related... Anyway, I let make finish :P and then rebooted... I installed it into my home directory... :-/ Testing it out now...

Karadoc ~~

Well, I didn't read the whole article... but I can tell you this: C is better than C++. :p

Quote:

string s0("my mother told me that");
string s1("cute");
string s2("fluffy");
string s3("kittens");
string s4("are an essential part of a healthy diet");

And that you concatenate them like this:

string dest = s0 + " " + s1 + " " + s2 + " " + s3 + " " + s4;

...
Each call to operator+() returns a temporary string. There are 8 calls to operator+() , so there are 8 temporary strings. Each one, upon its construction, performs a dynamic memory allocation and copies all of the characters that have been concatenated so far, and later, upon its destruction, performs a dynamic memory deallocation.

'snprintf' in C doesn't have that problem (it has other problems instead). Remind me why are people extending an already bloated language? Do we really need even more ways to confuse people with our different coding styles in the same language?

</end flamebait>

axilmar
anonymous said:

Example, please. How do I take a pointer to a temporary and manage to pass it to a function (as a rvalue reference) at the same time? In addition, would you really take the address of a temporary?

For example:

#SelectExpand
1//accept rvalue reference 2void foo(const vector<int> &&v) { 3 //move semantics; v is emptied 4 vector<int> b(v); 5 6 //error 7 v[0]; 8} 9 10//return a temporary 11vector<int> bar() { 12 return vector<int>{1, 2, 3, 4}; 13} 14 15int main() { 16 //pass a temporary to a function 17 foo(bar()); 18}

The above is no different than using auto_ptrs really. Same problem.

Quote:

I don't entirely understand. Do you suggest that implementing copy-on-write (?) for classes is simpler? Could you show an example of how you'd implement a swap for a vector (and do rvalue references have only to do with swapping)?

class vector {
    char *data;
};

void swap(vector &a, vector &b) {
    swap(a.data, b.data);
}

Quote:

I have a feeling that a non-optional GC would just turn C++ into a completely different language (why not program in a GC language in the first place?) and optionally not using it would be out of the question if the standard way of doing things would be what you describe.

Indeed, but that's only because C++ contains lots of stuff to handle the memory management problem.

anonymous

The example with vector doesn't work as you think - that program doesn't do anything bad (at least with GCC 4.4.1). I rewrote it with my non-copyable-but-moveable class which shows that it takes an explicit move:

#SelectExpand
1#include <iostream> 2 3class X 4{ 5 int* p; 6public: 7 X(): p(0) { std::cout << "X()\n"; } 8 X(int n): p(new int(n)) { std::cout << "X(int)\n"; } 9 X(const X&) = delete; 10 X(X&& x): p(x.p) { x.p = 0; std::cout << "X(X&& x)\n"; } 11 X& operator=(const X&) = delete; 12 X& operator= (X&& other) 13 { 14 if (this != &other) { 15 delete p; 16 p = other.p; 17 other.p = 0; 18 } 19 std::cout << "operator=(X&&)\n"; 20 return *this; 21 } 22 ~X() {delete p; std::cout << "~X()\n"; } 23 void print() const 24 { 25 if (p) { 26 std::cout << *p << '\n'; 27 } else { 28 std::cout << "NULL\n"; 29 } 30 } 31}; 32 33//using a const X&&, it has no option but to use X(const X&) even with std::move 34void foo(X &&x) 35{ 36 //X y(x); //this doesn't compile: tries to use X(const X&) 37 X y(std::move(x)); //this moves from x using X(X&&) 38 x.print(); 39 y.print(); 40} 41 42//return a temporary 43X bar() 44{ 45 return X(10); 46} 47 48int main() 49{ 50 //pass a temporary to a function 51 foo(bar()); 52}

The standard's committee is very well aware of the issues of auto_ptr. Do you really think they would deprecate std::auto_ptr for these reasons only to build the exact same behaviour into more user-defined objects? (Or GCC's implementation of rvalue references is completely broken.)

I think my general guideline works here: don't std::move if you can't put the object back into a good state (assuming you need to use it later on) or, in case you don't care about the value any longer, unless you have a non-moving overload that takes const X& (if you don't have that it doesn't matter in this example, if the X is a temporary or not).

void swap(vector &a, vector &b) {
    swap(a.data, b.data);
}

This is basically how it is with vector and other standard containers today. But this only helps if you call swap1. I guess I should have asked for the implementation of a full std::vector (e.g with copy constructor, operator=, push_back, operator[]).

-----

1 With GCC 3.5 I discovered (wondering why my swap overload for a user-defined class is not called by std::sort) that the standard library uses the std::iter_swap algorithm for swapping values held in iterators, which was essentially:

template <class I1, class I2> iter_swap(I1 it1, I2 it2) {
    X temp = *it1;
    *it1 = *it2;
    *it2 = temp;
}

I just replaced that with a forwarded call to swap (which made for example sorting strings quite a bit faster, as std::string has a specialized swap):

swap(*it1, *it2);

However, looking at the implementation of GCC 4.4, it seems that my "fix" may not have been entirely correct. If I'm not mistaken that implementation forwards to swap only if the value_types of both iterators are identical, otherwise doing something like:

X x(move(*it1));
*it1 = move(*it2);
*it2 = move(x);

I suppose that if the value_types are not the same (but convertible to each other), one might get annoying error messages complaining that the algorithm cannot choose the swap instantiation (since both types have to be the same). I didn't run into such situations, though, but I suppose this could happen with algorithms that work with 2 different ranges.

'snprintf' in C doesn't have that problem (it has other problems instead). Remind me why are people extending an already bloated language? Do we really need even more ways to confuse people with our different coding styles in the same language?

</end flamebait>

There should be quite a number of ways to get better performance:

#SelectExpand
1//1 operator+= 2string dest; 3dest.reserve(lenght_of_concatenated_string); 4dest += a; 5dest += " "; 6dest += b; 7... 8 9//2 stringstream (but doesn't stringstream still need to reallocate the buffer?) 10stringstream ss; 11ss << a << " " << b << ...; 12string dest = ss.str(); 13 14//3 expression templates (unless implemented by STL writers might look something like: 15//tons of complex templates here 16 17string dest = concatenator() + a + " " + b + " " + ...;

The C++ alternatives exist because alternative C ways tend to be rather error-prone. But you can fall back to using them if they indeed provide the performance you absolutely require.

If the only rationale for rvalue references was performance of string concatenation, then that would be quite lame indeed.

axilmar
anonymous said:

The example with vector doesn't work as you think - that program doesn't do anything bad (at least with GCC 4.4.1).

I think 'const' should be removed from the parameter 'v' of function 'foo'. A const rvalue reference should not allow for move semantics normally, because constness would be violated.

//accept rvalue reference
void foo(vector<int> &&v) {
    //move semantics; v is emptied
    vector<int> b(v);
    //error
    v[0];
}

Quote:

The standard's committee is very well aware of the issues of auto_ptr. Do you really think they would deprecate std::auto_ptr for these reasons only to build the exact same behaviour into more user-defined objects?

Well, perhaps, but then why do they give an example of 'clone_ptr' that works like std::auto_ptr?

Quote:

This is basically how it is with vector and other standard containers today. But this only helps if you call swap1. I guess I should have asked for the implementation of a full std::vector (e.g with copy constructor, operator=, push_back, operator[]).

It's very easy:

struct VectorData {
};

class vector {
    shared_ptr<VectorData> data;
};

You get copy construction, assignment, etc for free.

string s0("my mother told me that");
string s1("cute");
string s2("fluffy");
string s3("kittens");
string s4("are an essential part of a healthy diet");

And that you concatenate them like this:

string dest = s0 + " " + s1 + " " + s2 + " " + s3 + " " + s4;

...
Each call to operator+() returns a temporary string. There are 8 calls to operator+() , so there are 8 temporary strings. Each one, upon its construction, performs a dynamic memory allocation and copies all of the characters that have been concatenated so far, and later, upon its destruction, performs a dynamic memory deallocation.

I don't see how move semantics allows for any significant number of less allocations/deallocations. At best, the buffer of the temporary result is reallocated using 'realloc'. At worst, the temporary buffer would be extended by allocating a new larger buffer, copying the data and then freeing the old buffer. Still, nothing that could not be achieved with implicit sharing.

anonymous
axilmar said:

I think 'const' should be removed from the parameter 'v' of function 'foo'. A const rvalue reference should not allow for move semantics normally, because constness would be violated.

No, even without the const it doesn't move anything (my example above clearly shows that it wants to use the X(const X&) constructor). I may be mistaken but it would make sense if the general guideline concerning move semantics would be: if it has a name, then you need to ask explicitly for moving; things are implicitly moved only if they really don't have a name. (One must read and reread the linked article because it explains in great detail what binds to what, but seems to be lacking such general guidelines...)

Quote:

Well, perhaps, but then why do they give an example of 'clone_ptr' [www.artima.com] that works like std::auto_ptr?

It doesn't. Again, if my interpretation is correct, it only takes the short-cut implicitly if we are talking about true temporaries, e.g:

clone_ptr<X> x = foo();

Quote:

It's very easy:

Ok, and if I want a copy (e.g what happens if I pass it by value). What happens if I do:

vector<X> v1;
vector<X> v2 = v1;
...
v2[0] = X(...); //does it affect v1?

Quote:

I don't see how move semantics allows for any significant number of less allocations/deallocations. At best, the buffer of the temporary result is reallocated using 'realloc'. At worst, the temporary buffer would be extended by allocating a new larger buffer, copying the data and then freeing the old buffer. Still, nothing that could not be achieved with implicit sharing.

Probably also depends on the allocation strategy? Still, I'd love to see an example with implicit sharing.

BTW, they say that implementators have given up trying to optimize std::string with COW, since it has turned out to be a pessimization (if you have COW in mind)? Some thread-safety issues?

axilmar
anonymous said:

No, even without the const it doesn't move anything (my example above clearly shows that it wants to use the X(const X&) constructor). I may be mistaken but it would make sense if the general guideline concerning move semantics would be: if it has a name, then you need to ask explicitly for moving; things are implicitly moved only if they really don't have a name. (One must read and reread the linked article because it explains in great detail what binds to what, but seems to be lacking such general guidelines...)

Ok. Even if you do

//accept rvalue reference
void foo(vector<int> &&v) {
    //move semantics; v is emptied
    vector<int> b(move(v));
    //error
    v[0];
}

there is still a problem. The whole operation that empties v might be buried into some other code.

Quote:

It doesn't. Again, if my interpretation is correct, it only takes the short-cut implicitly if we are talking about true temporaries, e.g:

clone_ptr<X> x = foo();

It does. Check the following piece of code:

clone_ptr p1(new derived);
// ...
clone_ptr p2 = std::move(p1);  // p2 now owns the pointer instead of p1
p1->foo(); //boom!!!

Exactly the same as:

auto_ptr<derived> p1(new derived);
auto_ptr<derived> p2 = p1;  // p2 now owns the pointer instead of p1
p1->foo(); //boom!!!

Quote:

Ok, and if I want a copy (e.g what happens if I pass it by value). What happens if I do:

vector<X> v1;
vector<X> v2 = v1;
...
v2[0] = X(...); //does it affect v1?

You clone the object by using a specific function.

Quote:

Probably also depends on the allocation strategy? Still, I'd love to see an example with implicit sharing.

Check out QString.

Quote:

BTW, they say that implementators have given up trying to optimize std::string with COW, since it has turned out to be a pessimization (if you have COW in mind)? Some thread-safety issues?

Example? link?

anonymous
axilmar said:

there is still a problem. The whole operation that empties v might be buried into some other code.

I still don't see how that is any worse than any other bug buried in some other code.

As far as I understand there seems to be a rather simple guideline how to use move semantics safely. You need two overloads: one that picks up temporaries with && parameter which you can safely move since nobody will be missing them, and another that picks up other things with (const)& which you shouldn't move. (Or the function is like swap in that eventually everything is back to normal.) The clone_ptr has those overloads (constructor and assignment), my X class has those (except copy constructor/copy assignment are disabled), std::string::operator+ has those overloads etc.

Part of your misunderstanding seems to be that var is a rvalue in the following:

void foo(X&& var);

whereas it seems to me that this picks up temporaries (and other things if there are no more overloads), but var itself is lvalue which won't be implicitly affected by move semantics. rvalue references let you implement move semantics for your classes (a means to separate temporaries from named variables), but they don't by themselves move things.

Quote:

It does. Check the following piece of code

But the crucial difference is that with clone_ptr you explicitly ask it to be moved. It's not really that much different from the following (except this is a much nastier bug - an object which has been cleaned by moving at least is supposed to be in some consistent state, so it can at least be destroyed if not reused):

X* p = new X;
...
delete p;
...
*p;

Quote:

You clone the object by using a specific function.

Unfortunately, changing completely the semantics of existing libraries is out of the question.

Quote:

Example? link?

It is discussed here.

axilmar
anonymous said:

I still don't see how that is any worse than any other bug buried in some other code.

It is worse because the move might be implicit, i.e. programmer A inserts an optimization using move semantics, programmer B does not know that programmer A inserted an optimization, the application crashes.

Quote:

one that picks up temporaries with && parameter which you can safely move since nobody will be missing them

That is the problem: they might go missed.

Quote:

whereas it seems to me that this picks up temporaries (and other things if there are no more overloads), but var itself is lvalue which won't be implicitly affected by move semantics. rvalue references let you implement move semantics for your classes (a means to separate temporaries from named variables), but they don't by themselves move things.

Yes, I know, rvalue references do not move things by themselves, you have to implement the move operation. The problem is that moving data around is problematic: you don't know where a reference to a temporary might be stored.

Quote:

But the crucial difference is that with clone_ptr you explicitly ask it to be moved.

Don't be confused because you see std::move being called. I might have done the following:

clone_ptr p1(new derived);
// ...
clone_ptr p2 = haha(p1);  // p2 now owns the pointer instead of p1
p1->foo(); //boom!!!

and have the same results. No move insight, data of p1 are explicitly moved inside the function 'haha'.

It's not really that much different from the following

Exactly.

Unfortunately, changing completely the semantics of existing libraries is out of the question.

Why? progress requires sacrifices.

anonymous said:

BTW, they say that implementators have given up trying to optimize std::string with COW, since it has turned out to be a pessimization (if you have COW in mind)? Some thread-safety issues?

I read the material you posted. It's obvious: in all operations where COW is required, you have to lock the existing shared instance of data for as long as the operation takes place. For example, if you need to change a character in a string, you have to lock the instance, change the character, unlock the instance, in order to disallow modification from another thread. If another thread needs to modify the same string concurrently, the other thread needs to wait for the first thread to finish its operation before attempting to use the string.

The real problem lies with the implementation of strings though. In Java, strings are immutable for a reason, i.e. to avoid the overhead mentioned in the article you posted.

By the way, move semantics are dangerous when using threads as well.

anonymous
axilmar said:

It is worse because the move might be implicit, i.e. programmer A inserts an optimization using move semantics, programmer B does not know that programmer A inserted an optimization, the application crashes.

I agree that you still need to know what you are doing. You can't simply move things because you just feel like it. (If one gets burnt by that a couple of times, perhaps one will stop moving things without being sure that it is valid.)

Quote:

That is the problem: they might go missed.

Yes, I know, rvalue references do not move things by themselves, you have to implement the move operation. The problem is that moving data around is problematic: you don't know where a reference to a temporary might be stored.

How can one miss temporaries. A temporary is something that there cannot be any more references to? (A temporary is not a local variable - it is the return value of a function or a literal or an unnamed object like x = X(...))

Or may-be you have something like this in mind:

#SelectExpand
1//X class from previous posts 2 3class Y 4{ 5 X x; 6public: 7 Y(): x(10) {} 8 X&& get() { return x; } 9}; 10 11int main() 12{ 13 Y y; 14 X x = y.get(); 15 x.print(); 16 y.get().print(); 17}

It seems, the general guideline derived from this would be: don't give out rvalue references to things that you don't want to be moved from you (similarly like you wouldn't give out a non-const reference to something that you didn't want modified). If get returned a X or a X&, it would call the X(const X&) constructor - which is disabled for X.

Quote:

No move insight, data of p1 are explicitly moved inside the function 'haha'.

Fire the programmer responsible for haha?

There can be bugs everywhere.

Quote:

Why? progress requires sacrifices.

I suppose one could provide an alternative library with different names (and then get lots of whining from people about why there are so many alternatives to do the same thing), but can you really imagine that the meaning of each and every line using standard libraries changed radically with a new implementation? A few days ago I replaced a Vector2D overloading operators with free functions operating on tuples in Python: it took a couple of hours, fixing dozens of errors and bugs which all basically resulted from operator+ changing its meaning in some 1000 lines of code.

Quote:

The real problem lies with the implementation of strings though. In Java, strings are immutable for a reason, i.e. to avoid the overhead mentioned in the article you posted.

I haven't got this immutable strings thing. So, if I want to convert a single character to uppercase in a string (not an uncommon thing to do), do I get a whole new string (somehow without any overhead) or is it still basically COW?

--

All in all, it seems that your general complaint about C++0x is that it doesn't magically turn C++ in a safe language where you don't need to know what you are doing. This seems a bit unreasonable expectation with a language that is C++. Instead what you get with C++0x is that certain things are easier to do if you know what you are doing.

axilmar
anonymous said:

A temporary is not a local variable

A temporary is allocated on the stack if does not fit a machine register - which makes it a local variable. This variable may be unnamed when returned, but it can be passed as an argument to a function and therefore get a name. Then it might be moved (by explicitly calling std::move) and therefore cause a problem.

Quote:

Fire the programmer responsible for haha?

It's cool to joke like this, unless it is you that is going to be fired. Add a loan to that, and a wife and kids, and you will see it's not as rosy as you think.

Quote:

I suppose one could provide an alternative library with different names (and then get lots of whining from people about why there are so many alternatives to do the same thing), but can you really imagine that the meaning of each and every line using standard libraries changed radically with a new implementation?

Or deprecate the existing STL (or parts of it) and use new code. Nobody uses std::string anyway.

Quote:

I haven't got this immutable strings thing. So, if I want to convert a single character to uppercase in a string (not an uncommon thing to do), do I get a whole new string (somehow without any overhead) or is it still basically COW?

If you want to process strings in Java, you use a StringBuffer object.

Quote:

All in all, it seems that your general complaint about C++0x is that it doesn't magically turn C++ in a safe language where you don't need to know what you are doing. This seems a bit unreasonable expectation with a language that is C++. Instead what you get with C++0x is that certain things are easier to do if you know what you are doing.

All in all, my general complaint about C++ is that safety comes second, performance first, where it should have been the opposite. Performance can be increased by throwing more money to the problem, safety can not. Safety costs billions of dollars each year to the IT industry.

Sevalecan

Whine, whine, whine.

As for me, nothing about the C++ language itself really bothers me, and I'd love to see it extended.

However, the standard library is a bit lacking. I mean.. What if I don't want to use the PITA C Berkeley sockets interface? Gotta go grab a wrapper library, all of them seem pretty crappy. They don't know how to balance a good api with functionality.

Boost has some really neat stuff, but it's just... really big.. Though I suppose it's ok if you're not running Gentoo, Lunar Linux, or Sourcemage and thus don't have to compile it yourself. God does it take a long time to compile, and it uses alot of memory too. But wait! Half the libraries are all crammed into the headers anyway! What am I always spending so much time compiling then?

Qt also has some interesting stuff, and Qt 4 is pretty modular. But pretty much everything outside of QtCore needs the event loop running to even work. Ah just what I needed, to write a bunch of impractical code just to take advantage of the QHttp class in a very small program.

Could I at least ask for an auto_ptr that deals with arrays?

In spite of all that, C++ is still my primary language. Granted, most languages I know outside of it are scripting languages (perl, PHP, bash). Maybe some day I should try learning Java or C#.. Not that I would ever let C# or anything else steal my love for C++. I know how everyone always balks at me about how great they think C# is! WELL YOU'LL NEVER TAKE ME ALIVE!

bamccaig
axilmar said:

Don't be confused because you see std::move being called. I might have done the following...

Yes, you could have. But you should be fired and your code should be removed from the project for it. ;D Just like other low-level details, move semantics requires you to follow safe practices because just like deleting pointers that you don't own, they can be destructive if used wrongly. Just because you can turn an lvalue into an rvalue doesn't mean you necessarily should. I think move semantics are meant to be used sparingly in places where they shouldn't cause problems. If your method receives an rvalue then it should be expected that it actually refers to an rvalue (i.e., originally, it was a temporary). Further, you probably shouldn't implement move semantics in random functions or methods; at least not without being very explicit about it.

You pretty much need to go through std::move to turn the lvalue (which it becomes as soon as you pass it) back to an rvalue for it to have any affect under normal circumstances (obviously a function or method accepting T & shouldn't be passing it into std::move because as far as it knows it isn't an rvalue) so you will know that it's being moved.

anonymous
axilmar said:

A temporary is allocated on the stack if does not fit a machine register - which makes it a local variable. This variable may be unnamed when returned, but it can be passed as an argument to a function and therefore get a name. Then it might be moved (by explicitly calling std::move) and therefore cause a problem.

You can actually explicitly move stuff if it passed by regular reference or by value (in the latter case the caller will naturally be uneffected). The question is, where will moving happen implicitly. (I would sort of trust the standard committee has considered all kinds of scenarios to be sure that it cannot hurt you "mysteriously". Or perhaps we can come up with a scenario that would make them take it back. "Explicitly moving things moves things even if I don't want to move them" doesn't seem enough to convince them.

Quote:

It's cool to joke like this, unless it is you that is going to be fired. Add a loan to that, and a wife and kids, and you will see it's not as rosy as you think.

I didn't mean to imply that people should be fired over a single bug they produce, but if this hypothetical person keeps repeating the same mistake over and over despite being told (how) to stop abusing rvalue references and std::move, wouldn't he be happier in a different line of work (or with a different language). I don't entirely understand why such low standards should be acceptable in the programming trade (employee cannot and doesn't want to learn new things, cannot follow moderately simple guidelines, doesn't really know what he's doing). How come someone has been a successful C++ programmer who doesn't screw up badly all along and then suddenly turns out incapable of "getting" rvalue references despite repeated failures in critical projects?

Quote:

Nobody uses std::string anyway.

Well, not in programs using unicode. (I've heard that string is no good for that but mostly because GUI libraries seem to come with their own string class that is used everywhere, so it would be more effort to use std::string anyway.)

But is there such a perfect replacement? (For example, the reference that you linked says this about operator[]: "If i is beyond the length of the string then the string is expanded with QChar::nulls, so that the QCharRef references a valid (null) character in the string." Well, it is sort of safe but is this really something that you'd actually want to happen?)

Quote:

If you want to process strings in Java, you use a StringBuffer object.

I still don't get how I'd start with s = "Hello" and end up with s = "hello" without any overhead in between compared to s[0] = tolower(s[0]) with mutable strings. (I could understand how this StringBuffer is good if you want to modify many characters in the string.)

Quote:

Performance can be increased by throwing more money to the problem, safety can not. Safety costs billions of dollars each year to the IT industry.

I couldn't tell not being a programmer (and I suppose I'm lucky :)). Is there any statistics comparing money thrown to it to increase performance vs safety costs? How much of these safety costs come directly from C++ being unsafe? (I suppose you can write buggy programs in any language, except perhaps bugs are simpler/faster to discover/fix in safer languages if there happens to be anyone competent around.)

bamccaig
axilmar said:

By the way, move semantics are dangerous when using threads as well.

I could be being shortsighted, but I don't see how move semantics are going to have any affect on threading. They should only be used on temporary values, which means nothing else in the universe even knows of its existence, including other threads.

anonymous said:

X&& get() { return x; }

I could be wrong, but I don't think that's valid. x is an lvalue. The only way to make it work is to wrap x in std::move or something similar to explicitly change it to an rvalue. Which would be wrong.

axilmar
bamccaig said:

Yes, you could have. But you should be fired and your code should be removed from the project for it. ;D Just like other low-level details, move semantics requires you to follow safe practices because just like deleting pointers that you don't own, they can be destructive if used wrongly. Just because you can turn an lvalue into an rvalue doesn't mean you necessarily should. I think move semantics are meant to be used sparingly in places where they shouldn't cause problems. If your method receives an rvalue then it should be expected that it actually refers to an rvalue (i.e., originally, it was a temporary). Further, you probably shouldn't implement move semantics in random functions or methods; at least not without being very explicit about it.

You pretty much need to go through std::move to turn the lvalue (which it becomes as soon as you pass it) back to an rvalue for it to have any affect under normal circumstances (obviously a function or method accepting T & shouldn't be passing it into std::move because as far as it knows it isn't an rvalue) so you will know that it's being moved.

Your argument pretty much boils down to "use move safely". But you ignore a lot of factors: complexity, large number of lines of code, multiple programmers leaving and entering a project, changes after months or years etc.

anonymous said:

"Explicitly moving things moves things even if I don't want to move them" doesn't seem enough to convince them.

You saw the problems that moving might create above. Need I repeat myself?

Quote:

but if this hypothetical person keeps repeating the same mistake over and over despite being told (how) to stop abusing rvalue references and std::move, wouldn't he be happier in a different line of work (or with a different language)

It might be a single mistake done once, discovered late and cost millions.

Quote:

How come someone has been a successful C++ programmer who doesn't screw up badly all along and then suddenly turns out incapable of "getting" rvalue references despite repeated failures in critical projects?

Over a long period of time, destructive updating is dangerous. It just does not scale well.

Quote:

But is there such a perfect replacement?

They could have thrown an exception.

Quote:

I still don't get how...

Just use a StringBuffer, which is mutable.

bamccaig said:

I could be being shortsighted, but I don't see how move semantics are going to have any affect on threading.

What if two threads decide to move things from the same variable?

bamccaig
axilmar said:

They should have thrown an exception.

Fixed. ;)

axilmar said:

What if two threads decide to move things from the same variable?

They won't because you only move rvalues, which are temporary and inaccessible to the rest of the universe. If a programmer wants to break best practice and start passing lvalues around as rvalues then he'd better either know what he's doing (and even then, probably shouldn't) or hate his feet. Used correctly, the same temporary variable shouldn't be accessible to multiple threads. There is the potential for bugs, but no worse than with pointers.

axilmar
bamccaig said:

They won't because you only move rvalues, which are temporary and inaccessible to the rest of the universe. If a programmer wants to break best practice and start passing lvalues around as rvalues then he'd better either know what he's doing (and even then, probably shouldn't) or hate his feet. Used correctly, the same temporary variable shouldn't be accessible to multiple threads. There is the potential for bugs, but no worse than with pointers.

"Used correctly" is not the same as "compiler prevents it". We always try to do things correctly, but our programs are full of bugs, because we are humans and make mistakes. These problems must be diagnosed by the compiler.

Goalie Ca

I went through the standard documentations myself this morning and will summarize it in my own way (as clear as I can).

Quote:

Even though named rvalue references can bind to an rvalue, they are treated as lvalues when used.

Basically everywhere inside the function f(A&& a) a is now an lvalue. Why..

f(A&& a)
{
 g(a);
 h(a);
}

Well, in this case if a was treated as an rvalue then g(A&& a) and h(A&& a) would be called. G could basically pilfer a and then h and anything else would fail hard.

Being C++ though, with its ingenious workarounds such as const_cast, also includes casting from l-values to r-values!

Quote:

The client of a movable object might decide that he wants to move from an object even though that object is an lvalue. There are many situations when such a need might arise. One common example is when you have a full dynamic array of objects and you want to add to it. You must allocate a larger array and move the objects from the old buffer to the new. The objects in the array are obviously not rvalues. And yet there is no reason to copy them to the new array if moving can be accomplished much faster.

template <class T>
void
swap(T& a, T& b)
{
    T tmp(static_cast<T&&>(a));
    a = static_cast<T&&>(b);
    b = static_cast<T&&>(tmp);
}

So now we can swap data structures without writing our own swap functions inside T. Was it worth it? Who knows :P

Well. std::move is nothing more than a cast.

Quote:

Earlier this paper proposed that:
Named rvalue references are treated as lvalues.
This thought will now be completed:
Unnamed rvalue references are treated as rvalues.

#SelectExpand
1 template <class T> 2 inline 3 T&& 4 move(T&& x) 5 { 6 return static_cast<T&&>(x); 7 } 8 9 template <class T> 10 void 11 swap(T& a, T& b) 12 { 13 T tmp(move(a)); 14 a = move(b); 15 b = move(tmp); 16 }

In order for perfect forwarding, preserving r-values in case of r-values, you need to use std::move just as shown in previous example.

anonymous
axilmar said:

You saw the problems that moving might create above. Need I repeat myself?

Actually I didn't. It definitely is a new way to shoot yourself in the foot, but as seems habitual in C++, it still takes a moderate effort.

Your examples (if they compile or behave as you expect) seem very similar to:

delete p;
//OMG, *p is no more but I want to keep using it

except it seems to me that this sort of bug has much more severe consequences (what happens is not deterministic).

Quote:

It might be a single mistake done once, discovered late and cost millions.

Are you saying that you can't already make such a mistake in C++03 (or any other language) if deadlines are tight and people come and go? At least this is not a bug from undefined behaviour: if code asks object to be moved and object is moved, this happens every time (hopefully leaving NULL pointers and like behind).

Besides there seems to be a really simple cure against not being affected by move semantics: don't implement move constructor/assignment. How hard can that be?

Quote:

Just use a StringBuffer, which is mutable.

I didn't realize StringBuffer was an independent fully functional mutable string type. (I though it had something to do with a buffer of a String.) So, when I mostly don't modify I string, I use immutable String, and otherwise StringBuffer wins hands down. (I somehow got an impression earlier that you don't see a need for a mutable string if you have an immutable string.)

axilmar said:

These problems must be diagnosed by the compiler.

How is the compiler supposed to realize that you didn't want to move sth when you explicitly told it to move it?

bamccaig

Just finished reading part 1 too. Lambda's also look useful and I suppose the auto keyword will also help to keep code clean. static_assert sounds quite useful as well. I don't know what you people are complaining about. :P

Goalie Ca

I'm not convinced lambda is all that great on its own in a non-functional language like C++. The real benefit comes with closures but even so its not powerful enough because functions are not first class objects in this language :(

#SelectExpand
1//Both examples end up looking like a for loop. 2 3//the pos function over a vector 4//syntactically not too bad but auto would make something quite similar 5std::foreach(v.begin(), v.end(), [&](double a) { 6 if (a<0) 7 a = 0; 8}); 9 10//aah. but now we get interesting. notice how i don't use maximum_element because i can keep it simple and use the closure to cache the magnitude calculation. 11double max_so_far = 0; 12std::foreach(v.begin(), v.end(), [&](Vector4 a){ 13 double b = a.magnitude(); 14 if (b>max_so_far) 15 max_so_far = b; 16});

I'm still experimenting with other uses. The idea of supplying my own lambda function for sort or priority_queue is much more appealing than writing another function or having to hack something stupid into the operator<

edit: i forgot to mention that the whole point of using foreach and library functions is because you can parallelize and optimize it. notice the complete lack of explicit loop variables :-)

axilmar
anonymous said:

Are you saying that you can't already make such a mistake in C++03 (or any other language) if deadlines are tight and people come and go? At least this is not a bug from undefined behaviour: if code asks object to be moved and object is moved, this happens every time (hopefully leaving NULL pointers and like behind).

Of course you can. One more reason that C++1x is even more dangerous than existing C++.

Quote:

Besides there seems to be a really simple cure against not being affected by move semantics: don't implement move constructor/assignment. How hard can that be?

What about those in the STL? or other libraries? a vector move, for example, might be buried deep in some code.

Quote:

How is the compiler supposed to realize that you didn't want to move sth when you explicitly told it to move it?

There are ways to do it. Uniqueness typing, for example, allows for checking the sequence of mutations of a variable. Of course that's extremely advanced, on the forefront of programming language theory, not suitable for C++.

anonymous
axilmar said:

What about those in the STL? or other libraries? a vector move, for example, might be buried deep in some code.

Libraries are hopefully properly tested. Why do you trust std::vector even now as it is (if you do)? (You could write your own, so at least you'll know all bugs are made by you :))

For example, a std::vector would use moving when it resizes. If your class doesn't provide move semantics, then instances will be copied over as before. It would also seem that implementing a non-throwing move constructor is a lot simpler than implementing a non-throwing copy constructor (but I'm not sure how one would achieve the same kind of exception safety for vector resizing as it currently has if the move construction should throw nevertheless [a quick test shows at least this implementation doesn't leave the contents as they were when an exception is thrown from move constructor]).

axilmar
anonymous said:

Libraries are hopefully properly tested. Why do you trust std::vector even now as it is (if you do)? (You could write your own, so at least you'll know all bugs are made by you :))

Or so you think.

EDIT:

C++0x has one big positive thing: lambdas. Extremely extremely extremely useful, and C++0x worths the trouble for lambdas alone.

Here is a simple piece of code I wrote in Visual Studio 2010:

#SelectExpand
1#include <iostream> 2#include <vector> 3#include <algorithm> 4using namespace std; 5 6void haha(vector<int> &v) { 7 vector<int> v1 = move(v); 8} 9 10int wmain() { 11 vector<int> v; 12 v.push_back(1); 13 v.push_back(2); 14 v.push_back(3); 15 for_each(v.begin(), v.end(), [](int i) {cout << i << endl;}); 16 haha(v); 17 for_each(v.begin(), v.end(), [](int i) {cout << i << endl;}); 18 getchar(); 19 return 0; 20}

Notice how easy looping has become.

By the way, I wrote the above in order to test moving. The second loop prints nothing, as expected, because the input is emptied above. Major sh1t is going to hit the fan with this...

ImLeftFooted

I believe the primary benefit of move semantics is for very large classes.

class This{
  typedef char[2048] ThisStr;
  ThisStr strs[2048];
};

Imagine throwing that into swap and performing 3 copies!

As I understand, with move semantics a swap can be performed in a static amount of time.

anonymous

I believe the primary benefit of move semantics is for very large classes.

But how? I don't see how you could move an array (the contents of the array may be movable - you gain nothing from moving a char, though -, and there will apparently be a standard algorithm for moving them).

axilmar said:

By the way, I wrote the above in order to test moving. The second loop prints nothing, as expected, because the input is emptied above. Major sh1t is going to hit the fan with this...

And I say this sh1t is much less of a hassle than dangling pointers. As far as standard libraries are concerned, it is going to move only those things that nobody is going to miss (as move semantics are supposed to be used), such as when swapping things or in places like vector reallocation. The concern about SC++L giving you problems is 99.9% groundless.

Quote:

C++0x has one big positive thing: lambdas. Extremely extremely extremely useful, and C++0x worths the trouble for lambdas alone.

Indeed. Unfortunately it seems GCC doesn't have lambdas and range-for yet.

X-G
anonymous said:

But how? I don't see how you could move an array

Well, for instance, look at a std::vector. Right now, you cannot move a vector; you can only copy them. Copying an array of objects is a potentially very painful process, especially if it consists of objects with nontrivial copy semantisc of their own.

By contrast, the move semantics of std::vector is just assigning a pointer and a size counter (or two).

bamccaig
axilmar said:
void haha(vector<int> &v) {
  vector<int> v1 = move(v);
}

...

By the way, I wrote the above in order to test moving. The second loop prints nothing, as expected, because the input is emptied above. Major sh1t is going to hit the fan with this...

Yes, if you write bad code bad things will happen. You shouldn't be using std::move on an lvalue reference though so it's your own fault. Move semantics are something that won't stand out to the average programmer. Unless you intend to use them, you don't even need to know they exist (and many probably won't). The ones that do know should understand them before using them so this shouldn't happen much. If it does, hold those people accountable liable, just like you would for deleting a heap object that they don't own.

anonymous said:

Unfortunately it seems GCC doesn't have lambdas and range-for yet.

:(

anonymous
X-G said:

Well, for instance, look at a std::vector. Right now, you cannot move a vector; you can only copy them. Copying an array of objects is a potentially very painful process, especially if it consists of objects with nontrivial copy semantisc of their own.

By contrast, the move semantics of std::vector is just assigning a pointer and a size counter (or two).

I can see that. But I don't see how you can move a char[2048]. It is not a pointer, it is something that actually sits tight within the class.

X-G

Correct, but neither do you lose anything; copy semantics are already optimal. However, you can save significant amounts if you ever find yourself with an object like that dynamically allocated in the free store ("heap"), under the right circumstances.

anonymous

Coming back to the earlier example:

#SelectExpand
1//X class from previous posts 2class Y 3{ 4 X x; 5public: 6 Y(): x(10) {} 7 X&& get() { return x; } 8}; 9 10int main() 11{ 12 Y y; 13 X x = y.get(); //this uses move constructor 14 x.print(); //10 15 y.get().print(); //NULL 16}

bamccaig said:

I could be wrong, but I don't think that's valid. x is an lvalue. The only way to make it work is to wrap x in std::move or something similar to explicitly change it to an rvalue. Which would be wrong.

It seems that this is still valid (see last line):

Quote:

The && indicates an "rvalue reference". An rvalue reference can bind to either an rvalue or an lvalue:

  X a;
  X f();
  X& r1 = a;    // bind r1 to a (an lvalue)
  X& r2 = f();    // error: f() is an rvalue; can't bind

  X&& rr1 = f();  // fine: bind rr1 to temporary
  X&& rr2 = a;  // also fine: bind rr2 to a (an lvalue)

So one should be careful with returning &&. (I would have been more worried if returning by value allowed move construction here, but it seems existing rules about when copy construction can and cannot be elided kick in.)

bamccaig
anonymous said:

Coming back to the earlier example...

GCC (mainline revision 150667 on amd64 Linux) doesn't seem to agree with that code.

include/main.hpp#SelectExpand
1#ifndef MAIN_HPP 2 #define MAIN_HPP 3 4 #include <cstdlib> 5 #include <iostream> 6 #include <utility> 7 8int main(int, char * []); 9 10#endif

src/main.cpp#SelectExpand
1#include <main.hpp> 2 3class X 4{ 5 int * p; 6public: 7 X(void): 8 p(0) 9 { 10 std::cout << "X(void)\n"; 11 } 12 13 X(int n): 14 p(new int(n)) 15 { 16 std::cout << "X(int)\n"; 17 } 18 19 X(const X &) = delete; 20 21 X(X && x): 22 p(x.p) 23 { 24 x.p = 0; 25 std::cout << "X(X && x)\n"; 26 } 27 28 X & operator=(const X &) = delete; 29 30 X & operator=(X && other) 31 { 32 if(this != &other) 33 { 34 delete p; 35 p = other.p; 36 other.p = 0; 37 } 38 39 std::cout << "operator=(X &&)\n"; 40 41 return *this; 42 } 43 44 ~X(void) 45 { 46 delete p; 47 std::cout << "~X(void)\n"; 48 } 49 50 void print(void) const 51 { 52 if(p) 53 { 54 std::cout << *p << '\n'; 55 } 56 else 57 { 58 std::cout << "NULL\n"; 59 } 60 } 61}; 62 63class Y 64{ 65 X x; 66 67public: 68 Y(void): 69 x(10) 70 { 71 } 72 73 X && get(void) 74 {
75 return x;
76 } 77}; 78 79int main(int argc, char * argv[]) 80{ 81 Y y; 82 X x = y.get(); 83 84 //this uses move constructor 85 x.print(); //10 86 y.get().print(); //NULL 87 88 return EXIT_SUCCESS; 89}

[bamccaig@sephiroth cpp-rvalue-safety-check2]$ make
if [ ! -d obj ]; then \
                mkdir -p obj; \
        fi;
g++-r150667 -c -Iinclude -std=c++0x -o obj/main.o src/main.cpp
src/main.cpp: In member function ‘X&& Y::get()’:
src/main.cpp:75:16: error: invalid initialization of reference of type ‘X&&’ from expression of type ‘X’
make: *** [obj/main.o] Error 1
[bamccaig@sephiroth cpp-rvalue-safety-check2]$ 

Note line 75. x would need to be cast to an rvalue, for example, with std::move, making the decision explicit and punishable by death.

anonymous said:

It seems that this is still valid (see last line)...

GCC (mainline revision 150667 on amd64 Linux) doesn't seem to agree with that code either. Are you sure the source is correct?

include/main.hpp#SelectExpand
1#ifndef MAIN_HPP 2 #define MAIN_HPP 3 4 #include <cstdlib> 5 #include <iostream> 6 #include <utility> 7 8int main(int, char * []); 9 10#endif

src/main.cpp#SelectExpand
1#include <main.hpp> 2 3int main(int argc, char * argv[]) 4{ 5 int a; 6 int f(); 7 int& r1 = a; // bind r1 to a (an lvalue) 8 //int& r2 = f(); // error: f() is an rvalue; can't bind 9 10 int&& rr1 = f(); // fine: bind rr1 to temporary 11 int&& rr2 = a; // also fine: bind rr2 to a (an lvalue) *** 12 13 return EXIT_SUCCESS; 14}

[bamccaig@sephiroth cpp-rvalue-safety-check]$ make
if [ ! -d obj ]; then \
                mkdir -p obj; \
        fi;
g++-r150667 -c -Iinclude -std=c++0x -o obj/main.o src/main.cpp
src/main.cpp: In function ‘int main(int, char**)’:
src/main.cpp:11:17: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘int’
make: *** [obj/main.o] Error 1
[bamccaig@sephiroth cpp-rvalue-safety-check]$

Note that line 11 is in fact not fine.

Note: I realize that in both instances include/main.hpp serves little to no purpose. :P

axilmar
anonymous said:

The concern about SC++L giving you problems is 99.9% groundless.

It's not. Many programmers try to optimize their programs in any place they can. Imagine if they learn that they can speed up their programs by moving things, instead of copying them! moving things around that much is the definitive recipie for trouble!!!

bamccaig said:

Yes, if you write bad code bad things will happen.

The point of computers and computer languages is to do reliably what humans do unreliably. If a programming language does not stop me from doing the wrong things, then the programming language is not that good.

Thomas Fjellstrom

If you keep doing wrong things, you're not that good*.

  • and should be fired.

anonymous
bamccaig said:

GCC (mainline revision 150667 on amd64 Linux) doesn't seem to agree with that code.

Ok, sorry for the confusion. This seems to be some recent change in the rules and the FAQ might be out of date (hello Stroustrup!!!). In any case this is an important change that improves safety significantly. It's hard to get a very good picture before the whole thing is finalized and out-of-date tutorials pushed to the background.

axilmar said:

Imagine if they learn that they can speed up their programs by moving things, instead of copying them! moving things around that much is the definitive recipie for trouble!!!

I have always found that the following is good for optimizing memory usage:

vector<X*> v;
//create the objects
while (whatever)
{
    X* x = new X;
    v.push_back(x);
    delete x; //saves memory and avoids memory leaks
}
...

Could this hurt me? :)

SiegeLord

and should be fired.

So he's fired... who gets to fix the broken code he wrote? In the end, someone has to, and that person surely does not appreciate more pitfalls added to the language.

You can do all your best to make your code safe, but if you are working with 20 other people of perhaps inferior discipline, what do you do? Have them all be fired? Who writes the code then?

More features added to C++ mean less people known how to use all of it properly, therefore less people will be able to write good code (using all the features of the language) or understand other people's code (if the subsets of the language people use are not overlapping).

So, the solution is either for everyone to learn C++ (uneconomical, as learning the language = not developing and thus not earning money), or restrict the development of the program to some common subset (probably impossible in all situations, plus C++ guru's feel restricted).

So... there is no viable solution for this issue. C++ is fine if you develop alone... not so much when you develop with other people.

X-G
SiegeLord said:

So, the solution is either for everyone to learn C++ (uneconomical, as learning the language = not developing and thus not earning money)

... so you don't think C++ programmers need to actually know C++? I'm sorry, but that's incredibly idiotic. It's not that difficult of a language to learn, anyway.

Quote:

C++ is fine if you develop alone... not so much when you develop with other people.

Yes, that's why no major, extremely successful piece of software was ever written in C++. ::)

SiegeLord

Replace C++ by C++0x in my post.

X-G said:

It's not that difficult of a language to learn, anyway.

A matter of opinion, don't you think? You yourself might be smart enough to learn it, what about your coworkers? I clearly see that the Programming Questions part of allegro.cc has plenty of C++ questions. Are you saying that you are guaranteed never to work with one of such people in your company?

Quote:

Yes, that's why no major, extremely successful piece of software was ever written in C++.

Well, if there is no problem with the language (one of the two solutions I posted have been implemented) then there is no problem.

But how are you going to implement those two solutions when C++0x arrives? You either have to re-train everyone to know the new features, or forbid using the new features altogether.

X-G
SiegeLord said:

You yourself might be smart enough to learn it, what about your coworkers?

My coworkers are just as good as I, or in some cases much better. Those that can't handle C++ don't stay for long.

Quote:

You either have to re-train everyone to know the new features

Any company -- or indeed, programmer! -- that does not keep up with modern technology is doomed to fail regardless, and I wouldn't have it any other way. Stagnation ruins a lot of things. You must always continue to adapt and improve -- it's a Red Queen's game out there.

SiegeLord

One thing I forgot, what about external libraries? Can you vouch for those people too? Or do you never use external libraries?

And good for you and your coworkers, not everyone has such good surroundings as you do.

X-G

You're right, they don't -- and they should blame their coworkers, not their tools. I reiterate: C++ is not a difficult language to learn. I am constantly amazed by people struggling to comprehend the simplest of concepts.

gnolam
anonymous said:
vector<X*> v;
//create the objects
while (whatever)
{
    X* x = new X;
    v.push_back(x);
    delete x; //saves memory and avoids memory leaks
}
...

Could this hurt me? :)

Err... it should. The pointer pushed back into v now points to invalid memory...

bamccaig

I was hoping that was sarcasm... :-/

anonymous

I suppose that was to show that there is a difference between honest human error and outright cluelessness.

However, axilmar may have a point, even though IMO he hasn't put it quite clearly. (Demonstrating some misunderstanding of the topic and somewhat dogmatic fear of moving things in general.)

Suppose we have a following function.

void foo(X&& x)
{
    a(x);
    ...
    b(x);
    ...
    c(std::move(x));
}

As we know x comes to us from a temporary (assuming that nothing else can bind to &&), we may have decided to avoid passing it by value to c, seeing that it isn't going to be needed any more. If it now turns out that more code is to be added (or existing code reordered), we might indeed easily end up with something like:

void foo(X&& x)
{
    a(x);
    ...
    b(x);
    ...
    c(std::move(x));
    ...
    d(x); //dang
    ...
}

However, I'd still say that this evil is far lesser than what you can get from pointers gone bad. The behavior of the program will be far more deterministic and the error should be far easier to discover and fix. Besides, does one indeed make modifications to a large and complicated function without taking some time first to see what it does? And if you really wanted to optimize, wouldn't it be better if there was one or two well-known and robust idioms, rather than a plethora of ad hoc hacks that might be otherwise invented?

axilmar

The move capability of C++1x will increase performance of the language in speed tests, but it does not scale to many developers, multiple changes and projects that take years to complete. It's simply a mistake of Stroustrup and Co. that they will regret a few years from now, just like they did with std::auto_ptr.

The Academia literally screams about how destructive updating is harmful to compilers and CPU performance and parallelization/concurrency, yet Stroustrup and Co. put more destructive updating in the language...

anonymous

May-be instead of appealing to some "Academy", you give a link to some article that discusses the issues? It might be more enlightening.

Has auto_ptr wreaked havoc in practice (any stories?), rather than just being generally avoided? I haven't had any problems using it safely (e.g to hold a pointer in a non-copyable class, although I nowadays I tend to use boost::scoped_ptr instead - and unique_ptr would be good for noncopyable but moveable classes).

Sevalecan

Off hand, I can see no problems with auto_ptr. I mean, I'd really have to search the net to find out whatever it is you're complaining about. Assuming anyone there knows. I just see no obvious problems with a scoped pointer class, except maybe the fact that there isn't one for arrays or something.

Is it possible that you and all the people you're arguing for just suck at programming to some amazing degree that no half-witted person can even comprehend?

Goalie Ca
axilmar said:

The Academia literally screams about how destructive updating is harmful to compilers and CPU performance and parallelization/concurrency, yet Stroustrup and Co. put more destructive updating in the language...

Well being an academic and having published in this area bitching about the difficulties of state in multi-core I feel I have to clarify.

This std::move is a good thing for its intended use. This is yet another case of an optimization that can't really be infered by a compiler. There are a lot of people in the academic community who have been trying for decades to figure out how to get compilers to be smart enough to figure out code and do really smart things with it. The problem is that you can't really prove anything in a language like C++. You really need "annotations" added by human programmers in order to tell the compiler it's okay to do some things. In this case there is more to it than that because the programmer also has to supply the alternate code path as well. But the point is, there are a number of places where this will be a good optimization. I really doubt that C++ will be the standard multi-core language. It has another niche to fill.

axilmar
Sevalecan said:

Off hand, I can see no problems with auto_ptr. I mean, I'd really have to search the net to find out whatever it is you're complaining about. Assuming anyone there knows. I just see no obvious problems with a scoped pointer class, except maybe the fact that there isn't one for arrays or something.

Is it possible that you and all the people you're arguing for just suck at programming to some amazing degree that no half-witted person can even comprehend?

Perhaps. On the other hand: http://groups.google.com/group/comp.std.c++/browse_frm/thread/e60adecf1427635b?hl=en&pli=1

Quote:

I consider auto_ptr very useful, but error prone due to its
move-with-copy-syntax behavior.

The above is found in the first google results page.

anonymous

But rvalue references and auto_ptr are still a completely different thing? (The former is even a way to fix the latter: provide a really simple smart pointer that you can store safely in standard containers etc.)

The compiler will implicitly move only in places where you can't miss the original (those rules have been in place for a long time, e.g where copy constructor calls can be elided), and in addition rvalue references provide a sure way to determine cases where it is always safe to move explicitly. In addition there are a few specific cases where moving is OK due to the nature of the algorithm (swap, when resizing vector etc).

#include <vector>
#include <memory>

using namespace std;

int main()
{
    vector<unique_ptr<int>> v;
    unique_ptr<int> p (new int);
    v.push_back(p);  //compiler error: unique_ptr is non-copyable
    v.push_back(unique_ptr<int>(new int)); //OK, and you can't miss this unique_ptr
}

axilmar
anonymous said:

The compiler will implicitly move only in places where you can't miss the original.

You said that 3 times. Ok, I understood it, I agree with you. The problem is not that. The problem is how this is going to be used by the programmers. A programmer may explicitly move things around only to mess things up later. Trying to find where data are moved in code is extremely difficult, especially if the code is complex.

How about:

#include <vector>
#include <memory>

using namespace std;

int main()
{
    vector<unique_ptr<int>> v;
    unique_ptr<int> p (new int);
    v.push_back(p);  //compiler error: unique_ptr is non-copyable
    v.push_back(foo(unique_ptr<int>(new int))); //not OK; foo may keep a pointer to the data.
}

anonymous

And you have mentioned this a couple of times as well. Provide a real example of what foo would have to look like without a dumb use of std::move and then keeping using the same variable.

Yes, given a large and messy codebase written by people that come and go, I can imagine that any line can contain any arbitrary hidden bug. Misuse of std::move seems to be on the benign end of things that could go wrong.

What I'm saying that rvalue references seem to protect you quite nicely from honest human errors, but not from incompetence, and are therefore much easier and safer to use than many other existing features.

axilmar
anonymous said:

And you have mentioned this a couple of times as well. Provide a real example of what foo would have to look like without a dumb use of std::move and then keeping using the same variable.

Yes, given a large and messy codebase written by people that come and go, I can imagine that any line can contain any arbitrary hidden bug. Misuse of std::move seems to be on the benign end of things that could go wrong.

What I'm saying that rvalue references seem to protect you quite nicely from honest human errors, but not from incompetence, and are therefore much easier and safer to use than many other existing features.

I agree with you, except for the "misuse of std::move seems be on the benign end of things". Accessing memory previously deleted is one of the most difficult bugs to solve.

anonymous
axilmar said:

I agree with you, except for the "misuse of std::move seems be on the benign end of things". Accessing memory previously deleted is one of the most difficult bugs to solve.

But when an object has been moved from, it is not deleted memory in the same sense as what you get when you type delete p;. That object has to be in a good enough state, so at least its destructor can still run (e.g it contains NULL pointers, or I suppose it might have swapped the pointers).

Unlike misusing delete, where the language itself cannot check that you follow a sensible ownership policy, with rvalue references you have a good idea where std::move cannot hurt. The only thing to keep in mind is not to keep using the moved-from object, unless you put it in a good state again (mostly you'd move from things that are going out of scope anyway).

#include <cstdio>
#include <memory>

int main()
{
    std::unique_ptr<int> a(new int(10));
    std::unique_ptr<int> b(std::move(a));
    a.reset(new int(20));  //this should be OK
    printf("%d %d\n", *a, *b);
}

X-G

So let's stop using pointers altogether, eh?

kazzmir

Yea, welcome to Java/perl/python/ocaml/scheme/ruby/javascript/anything invented in the past 20 years.

axilmar
anonymous said:

The only thing to keep in mind is not to keep using the moved-from object

It does not scale well in time (projects taking months or years to complete and changes over time) as well in space (programmers coming and going etc). This is what I am telling you.

For a single programmer that can fit everything in his head, the problem is minimal. But for a big project, it's a disaster waiting to happen.

anonymous

Is there that much to fit into the head? Wouldn't it be enough to look at current function to know whether a move is good or not? (Completely unlike delete where either you have a very clear ownership policy or you are completely lost.)

void foo(X&& x)
{
    X other = std::move(x);
    x.bar(); //don't do this (using moved-from object)
}

void foobar(X& x)
{
    foo(std::move(x)); //don't do this either (moving from lvalue reference without restoring x)
}

bamccaig

If a particular team is and has been capable of developing software in C++ then I don't see move semantics causing them problems.

Thomas Fjellstrom
kazzmir said:

Yea, welcome to Java/perl/python/ocaml/scheme/ruby/javascript/anything invented in the past 20 years.

Perl has pointers. It calls them references, but its the same thing. And if you feel really dirty, you can actually use real addresses, and turn it into a perl object.

my $var = "SCALAR(0xdeadbeef)"; $$var = "foo"; # BAD!

edit: I realize thats not actually how you do it, but its something like that, and something I've only done once in the past 10+ years I've been codeing in perl, so I don't remember how its done.

kazzmir

You're right, perl is just as bad as C. I should have known.

axilmar
anonymous said:

Is there that much to fit into the head? Wouldn't it be enough to look at current function to know whether a move is good or not? (Completely unlike delete where either you have a very clear ownership policy or you are completely lost.)

It just does not scale well. If, in your example, the move call and the x.bar() call was a few pages apart, for example, then you might easily miss it.

Thread #601204. Printed from Allegro.cc