Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > C++0x: unique_ptr and std::move

Reply
Thread Tools

C++0x: unique_ptr and std::move

 
 
Micha³ 'Khorne' Rzechonek
Guest
Posts: n/a
 
      01-28-2009
Hello,

I wanted o understand how rvalue references work, so I took GCC 4.3
with -std=c++0x flag and wrote code below.

What I don't understand is why 2nd assertion fails and move ctor is
not
called. Please enlighten me

Side question: does source() function look all right?

#include <iostream>
#include <cassert>

using std::cout;
using std::endl;
using std::move;

template<typename T>
class unique_ptr {
public:
explicit unique_ptr(T *&&a_ptr): m_ptr(a_ptr) {
a_ptr = NULL;
}

unique_ptr(unique_ptr &&p): m_ptr(p.release()) {
cout << "Move" << endl;
}

T *release() {
T *ptr = m_ptr;
m_ptr = NULL;
return ptr;
}

T *get() {
return m_ptr;
}

T *operator->() {
return m_ptr;
}

~unique_ptr() {
if(m_ptr != NULL) {
delete m_ptr;
}
}

private:
unique_ptr(const unique_ptr &);
void operator=(const unique_ptr &);
void operator=(unique_ptr &&p);

T *m_ptr;

};

struct Foo
{
Foo(int a): a(a) {
cout << "Foo::ctor(" << a << ")" << endl;
}

~Foo() {
cout << "Foo::dtor()" << endl;
}

int a;
private:
Foo(const Foo &);
Foo(Foo &&);

};

unique_ptr<Foo> source(int a = 0) {
return move(unique_ptr<Foo>(new Foo(a)));

}

void sink(unique_ptr<Foo> a_foo) {
cout << a_foo->a << endl;

}

int main() {
unique_ptr<Foo> foo( source(1) );
unique_ptr<Foo> bar = move(foo);
assert(foo.get() == NULL); // ok

unique_ptr<Foo> qux( source(2) );
sink( move(qux) );
assert(qux.get() == NULL); // ??
}
 
Reply With Quote
 
 
 
 
Niels Dekker - no return address
Guest
Posts: n/a
 
      01-28-2009
Michal 'Khorne' Rzechonek wrote:
> I wanted o understand how rvalue references work, so I took GCC 4.3
> with -std=c++0x flag and wrote code below.
>
> What I don't understand is why 2nd assertion fails and move ctor is
> not called. Please enlighten me
>
> #include <iostream>
> #include <cassert>
>
> using std::cout;
> using std::endl;
> using std::move;
>
> template<typename T>
> class unique_ptr {
> public:
> explicit unique_ptr(T *&&a_ptr): m_ptr(a_ptr) {
> a_ptr = NULL;
> }
>
> unique_ptr(unique_ptr &&p): m_ptr(p.release()) {
> cout << "Move" << endl;
> }
>
> T *release() {
> T *ptr = m_ptr;
> m_ptr = NULL;
> return ptr;
> }
>
> T *get() {
> return m_ptr;
> }
>
> T *operator->() {
> return m_ptr;
> }
>
> ~unique_ptr() {
> if(m_ptr != NULL) {
> delete m_ptr;
> }
> }
>
> private:
> unique_ptr(const unique_ptr &);
> void operator=(const unique_ptr &);
> void operator=(unique_ptr &&p);
>
> T *m_ptr;
>
> };
>
> struct Foo
> {
> Foo(int a): a(a) {
> cout << "Foo::ctor(" << a << ")" << endl;
> }
>
> ~Foo() {
> cout << "Foo::dtor()" << endl;
> }
>
> int a;
> private:
> Foo(const Foo &);
> Foo(Foo &&);
>
> };
>
> unique_ptr<Foo> source(int a = 0) {
> return move(unique_ptr<Foo>(new Foo(a)));
>
> }
>
> void sink(unique_ptr<Foo> a_foo) {
> cout << a_foo->a << endl;
>
> }
>
> int main() {
> unique_ptr<Foo> foo( source(1) );
> unique_ptr<Foo> bar = move(foo);
> assert(foo.get() == NULL); // ok
>
> unique_ptr<Foo> qux( source(2) );
> sink( move(qux) );
> assert(qux.get() == NULL); // ??
> }


I think the assertion should not fail. Could it be a compiler bug? It
reminds me of GCC Bugzilla Bug 36744 - function modifying argument
received by-value affects caller's variable when passed as rvalue
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36744


> Side question: does source() function look all right?


> unique_ptr<Foo> source(int a = 0) {
> return move(unique_ptr<Foo>(new Foo(a)));


Should work fine without the "move", as unique_ptr<Foo>(...) is an
rvalue already:

unique_ptr<Foo> source(int a = 0) {
return unique_ptr<Foo>(new Foo(a));

(Untested)


HTH, Niels

--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center



 
Reply With Quote
 
 
 
 
SG
Guest
Posts: n/a
 
      01-28-2009
On 28 Jan., 10:35, Micha³ 'Khorne' Rzechonek <(E-Mail Removed)>
wrote:
> Hello,
>
> I wanted o understand how rvalue references work, so I took GCC 4.3
> with -std=c++0x flag and wrote code below.


[rearranged]

> #include <iostream>
> #include <cassert>


Don't you need <utility> as well for std::move?

> using std::cout;
> using std::endl;
> using std::move;
>
> template<typename T>
> class unique_ptr {
> public:
> * * explicit unique_ptr(T *&&a_ptr): m_ptr(a_ptr) {
> * * * * a_ptr = NULL;
> * * }


That's unusual. But ok considering current rules. However, the
semantics of "&&" may change, see:
http://www.open-std.org/jtc1/sc22/wg...s/papers/2008/

I would use

explicit unique_ptr(T * a_ptr): m_ptr(a_ptr) {}

instead.

> * * unique_ptr(unique_ptr &&p): m_ptr(p.release()) {
> * * * * cout << "Move" << endl;
> * * }
>
> * * T *release() {
> * * * * T *ptr = m_ptr;
> * * * * m_ptr = NULL;
> * * * * return ptr;
> * * }
>
> * * T *get() {
> * * * * return m_ptr;
> * * }
>
> * * T *operator->() {
> * * * * return m_ptr;
> * * }


The above two functions (get, operator->) should be const. Is there no
overload for operator* ?

> * * ~unique_ptr() {
> * * * * if(m_ptr != NULL) {
> * * * * * * delete m_ptr;
> * * * * }
> * * }


You don't need to check for null pointers here.

>
> private:
> * * unique_ptr(const unique_ptr &);
> * * void operator=(const unique_ptr &);
> * * void operator=(unique_ptr &&p);


You don't need an extra && overload here for operator=.

> * * T *m_ptr;
>
> };
>
> struct Foo
> {


[snip]

> };
>
> unique_ptr<Foo> source(int a = 0) {
> * * return move(unique_ptr<Foo>(new Foo(a)));
> }
>
> void sink(unique_ptr<Foo> a_foo) {
> * * cout << a_foo->a << endl;
>
> }
>
> int main() {
> * * unique_ptr<Foo> foo( source(1) );
> * * unique_ptr<Foo> bar = move(foo);
> * * assert(foo.get() == NULL); // ok
>
> * * unique_ptr<Foo> qux( source(2) );
> * * sink( move(qux) );
> * * assert(qux.get() == NULL); // ??
>
> }


[rearranged]

> What I don't understand is why 2nd assertion fails and move ctor is
> not called. Please enlighten me


It fails? That's odd. I can't test it myself right now, unfortunately.
I guess it's either a compiler bug or we overlooked something.

> Side question: does source() function look all right?


Yes. You don't need the extra move(), though. You only need move() if
you want to return a function's parameter or some other lvalue
reference as rvalue. Local variables (not including call-by-value
parameters) are automatically treated as rvalues in a return
statement.

Cheers!
SG
 
Reply With Quote
 
Micha³ 'Khorne' Rzechonek
Guest
Posts: n/a
 
      01-28-2009
FYI: checked with GCC 4.4, assertion still fails.

On 28 Sty, 11:16, SG <(E-Mail Removed)> wrote:
> > public:
> > * * explicit unique_ptr(T *&&a_ptr): m_ptr(a_ptr) {
> > * * * * a_ptr = NULL;
> > * * }

>
> That's unusual. But ok considering current rules. However, the
> semantics of "&&" may change


Well, my intention was to move ownership from lvalues... I understand
your point though.

> > What I don't understand is why 2nd assertion fails and move ctor is
> > not called. Please enlighten me

>
> It fails? That's odd. I can't test it myself right now, unfortunately.
> I guess it's either a compiler bug or we overlooked something.


Debugging the code shows that both objects have the same address, so
it looks like ctor elision. I am not sure if move ctors can be eluded,
they have side effects "by default", don't they?

> You don't need the extra move(), though. You only need move() if
> you want to return a function's parameter or some other lvalue
> reference as rvalue. Local variables (not including call-by-value
> parameters) are automatically treated as rvalues in a return
> statement.


Thanks for detailed explanation.

Khorne
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      01-28-2009
I was able to cut it down to a very small piece of code. The problem
still exists. It seems to be a compiler bug.

#include <iostream>
#include <ostream>
#include <cassert>
#include <utility>

using std::cout;
using std::endl;
using std::move;

class movable {
int resource; // 0=not acquired
movable(const movable&); // no copy c'tor
void operator=(const movable&); // no copy-assignment
public:
explicit movable(int i=0) : resource(i) {}
int get() const {return resource;}
int release() {int r=resource; resource=0; return r;}
movable(movable && rval) : resource(rval.release())
{ cout << "movable::movable(movable &&)" << endl;}
};

void sink(movable m) {
cout << "sink: &m --> " << &m << endl;
cout << "sink: m.get() --> " << m.get() << endl;
}

int main() {
movable m (42);
cout << "main: &m --> " << &m << endl;
sink(move(m));
assert(m.get()==0);
}

The assertion fails with GCC 4.3.0. I can even remove the move-c'tor
and it still compiles.

sg@box:~/$ g++ --version
g++ (Ubuntu 4.3.2-1ubuntu11) 4.3.2
Copyright (C) 2008 Free Software Foundation, Inc.

sg@box:~/$ ./test
main: &m --> 0xbfd9eb00
sink: &m --> 0xbfd9eb00
sink: m.get() --> 42
test: test.cpp:37: int main(): Assertion `m.get()==0' failed.
Aborted

Note: The move constructor is not invoked. The interesting part is
that 'm' in main and 'm' in sink have the same address.

Cheers!
SG
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      01-28-2009
On 28 Jan., 22:17, SG <(E-Mail Removed)> wrote:
> I was able to cut it down to a very small piece of code. The problem
> still exists. It seems to be a compiler bug.
> [...]
> The assertion fails with GCC 4.3.0. I can even remove the move-c'tor
> and it still compiles.
> [...]
> Note: The move constructor is not invoked. The interesting part is
> that 'm' in main and 'm' in sink have the same address.


Just to clearify: The fact that the compiler accepts the code with
copy- and move-c'tor being deleted (private) is probably a bug. G++
does a move/copy elision here which is IMHO impressive because move()
returns just a reference and a copy/move elision requires the
inspection of the move() function to figure out that the rvalue
reference refers to the local object 'm'. I honestly don't know
whether this is allowed by the current draft or not. With move() you
basically say "I don't care about what happens with 'm' here".

Cheers!
SG
 
Reply With Quote
 
Vidar Hasfjord
Guest
Posts: n/a
 
      01-29-2009
On Jan 28, 9:17 pm, SG <(E-Mail Removed)> wrote:
> I was able to cut it down to a very small piece of code. The problem
> still exists. It seems to be a compiler bug.
>
> #include <iostream>
> #include <ostream>
> #include <cassert>
> #include <utility>
>
> using std::cout;
> using std::endl;
> using std::move;
>
> class movable {
> int resource; // 0=not acquired
> movable(const movable&); // no copy c'tor
> void operator=(const movable&); // no copy-assignment
> public:
> explicit movable(int i=0) : resource(i) {}
> int get() const {return resource;}
> int release() {int r=resource; resource=0; return r;}
> movable(movable && rval) : resource(rval.release())
> { cout << "movable::movable(movable &&)" << endl;}
> };
>
> void sink(movable m) {
> cout << "sink: &m --> " << &m << endl;
> cout << "sink: m.get() --> " << m.get() << endl;
> }
>
> int main() {
> movable m (42);
> cout << "main: &m --> " << &m << endl;
> sink(move(m));
> assert(m.get()==0);
> }
>
> The assertion fails with GCC 4.3.0. I can even remove the move-c'tor
> and it still compiles.
>
> sg@box:~/$ g++ --version
> g++ (Ubuntu 4.3.2-1ubuntu11) 4.3.2
> Copyright (C) 2008 Free Software Foundation, Inc.
>
> sg@box:~/$ ./test
> main: &m --> 0xbfd9eb00
> sink: &m --> 0xbfd9eb00
> sink: m.get() --> 42
> test: test.cpp:37: int main(): Assertion `m.get()==0' failed.
> Aborted
>
> Note: The move constructor is not invoked. The interesting part is
> that 'm' in main and 'm' in sink have the same address.


I've experienced this bug as well. A simple workaround is:

void sink (movable&& r) {
movable m (move (r));
cout << "sink: &m --> " << &m << endl;
cout << "sink: m.get () --> " << m.get () << endl;
}

Regards,
Vidar Hasfjord
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      01-31-2009
I searched the GCC bug database and it seems there is already a report
on a similar case (#36744)

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36744

Even in the case of a missing move constructor the copy must not be
elided because the object in question is not a temporary.

Cheers!
SG
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Should `unique_ptr< T const [] >` accept a `T*` constructor argument? Alf P. Steinbach C++ 3 12-20-2011 04:55 PM
Marshalling auto_ptr/unique_ptr objects Brian C++ 1 03-06-2010 01:45 AM
boost::unique_ptr and problems with deletor argument Christopher C++ 5 03-18-2009 02:36 PM
Re: boost::unique_ptr and problems with deletor argument SG C++ 1 03-17-2009 12:19 PM
where is unique_ptr? Aaron Graham C++ 1 06-22-2008 03:26 PM



Advertisments