C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

unique_ptr

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards
View previous topic :: View next topic  
Author Message
l.j.persson@gmail.com
Guest





PostPosted: Fri Sep 02, 2005 5:11 am    Post subject: unique_ptr Reply with quote



Hello,
I was just reading the excellent proposal on unique_ptr
([url]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html)[/url].
But I have a question on the constructor: why doesn't it take a T*&&
parameter?

explicit unique_ptr(T*&& p);

If I understand rvalues correctly that would mean:

unique_ptr<int> ui(new int); // Ok
int* pi = new int;
unique_ptr<int> ui(pi); // Not Ok

In current implementation we have

int* pi = new int;
unique_ptr<int> ui1(pi); // Ok
unique_ptr<int> ui2(pi); // Ok, but no good

same applies to operator=() and reset().

/ Jonas

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Back to top
Howard Hinnant
Guest





PostPosted: Sat Sep 03, 2005 5:23 am    Post subject: Re: unique_ptr Reply with quote



In article <1125603471.570361.32720 (AT) o13g2000cwo (DOT) googlegroups.com>,
"l.j.persson (AT) gmail (DOT) com" <l.j.persson (AT) gmail (DOT) com> wrote:

Quote:
Hello,
I was just reading the excellent proposal on unique_ptr
([url]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html)[/url].
But I have a question on the constructor: why doesn't it take a T*&&
parameter?

explicit unique_ptr(T*&& p);

If I understand rvalues correctly that would mean:

unique_ptr<int> ui(new int); // Ok
int* pi = new int;
unique_ptr<int> ui(pi); // Not Ok

In current implementation we have

int* pi = new int;
unique_ptr<int> ui1(pi); // Ok
unique_ptr<int> ui2(pi); // Ok, but no good

same applies to operator=() and reset().

That's a very interesting suggestion!

I must admit that I'm waffling on it at the moment. At first I thought
it sounded great, and I prototyped it right up. But then I used that
prototype to start looking at some of my use cases, and my enthusiasm is
waning.

There are times when you really do want to assign an lvalue pointer to a
unique_ptr. In fact N1856 outlines one such case:

// A::foo establishes ownership of p, but
// must acquire other resources to do so. A local
// unique_ptr is used as an aid to hold on to p while
// those other resources are acquired.
template<class T, class D>
void
A<T, D>::foo(T* p)
{
// Establish preliminary ownership without requiring
// a copy of the deleter D
std::unique_ptr<T, D&> hold(p, deleter_); // no throw
// acquire resources // if throws,
// ... // deleter_(p) executed
// transfer ownership to A
p_ = hold.release(); // no throw
}

With your suggestion that would change to:

template<class T, class D>
void
A<T, D>::foo(T* p)
{
// Establish preliminary ownership without requiring
// a copy of the deleter D
std::unique_ptr<T, D&> hold(std::move(p), deleter_); // no throw
// acquire resources // if throws,
// ... // deleter_(p) executed
// transfer ownership to A
p_ = hold.release(); // no throw
}

Now that doesn't look too bad. But then I went back to the source, the
actual code that the A::foo example is derived from, and it begins to
look worse:

template<class T>
template<class Y, class D>
shared_ptr<T>::shared_ptr(Y* p, D&& d)
: ptr_(p)
{
Metrowerks::move_ptr<Y, D&> hold(p, d);
s_ = new detail::shared_ptr_deleter<Y, D>(p, d);
hold.release();
...
}

Now you can see that "p" is assigned several places in this code. Which
of those places should be changed to move(p)?

(Metrowerks::move_ptr is the proposed std::unique_ptr).

The fact is that after I "own" p with the unique_ptr, I subsequently
also pass ownership of p to the shared_ptr - without first relinquishing
ownership from the unique_ptr! And that is as it should be. So should
the code look like this:

template<class T>
template<class Y, class D>
shared_ptr<T>::shared_ptr(Y* p, D&& d)
: ptr_(p)
{
Metrowerks::move_ptr<Y, D&> hold(move(p), d);
s_ = new detail::shared_ptr_deleter<Y, D>(move(p), d);
hold.release();
...
}

I guess what I'm getting at, and not expressing too eloquently at the
moment, is that I believe accepting only rvalue pointers in the
unique_ptr ctor might lead to code that just liberally sprinkled move(p)
expressions around, and the resulting code is really no safer than it is
without the move(p) expressions.

Ok, gathering my thoughts a little more (maybe):

Assertion:

An rvalue pointer is not intrinsically safer to pass to a smart pointer
than is an lvalue pointer.

But this is a most interesting suggestion! And thank you for making it.
I would be most interested to continue this discussion and collect a
wide range of opinions. This is a feature I had not previously thought
about and we would be remiss not to fully explore it.

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Jonas Persson
Guest





PostPosted: Sun Sep 04, 2005 2:20 am    Post subject: Re: unique_ptr Reply with quote



I dont think it would sprinkle the code with move.
The only(?) places where you assign it lvaues is from a local variable:

int* i = new int:
unique_ptr<int> ip(move(i));

which is never necessary and not to be recomended, and from prameters:

void foo(int *i) {
unique_ptr<int> ip(move(i));
}

in which case the function resumes ownership of a pointer parameter.
That should never happen except for smart pointer ctors. The correct
signature of such a function should be: void foo(unique_ptr<int> i).

The major reason for unsafe code is memory owned by a naked pointer.
Making passing lvalues less convenient than rvalues makes using naked
pointers less convenient.

In your example, it is not nesessary to use move after the first one
anyway. Probably get() is a better choise:

template<class T>
template<class Y, class D>
shared_ptr<T>::shared_ptr(Y* p, D&& d)
: ptr_(p)
{
Metrowerks::move_ptr<Y, D&> hold(move(p), d);
s_ = new detail::shared_ptr_deleter<Y, D>(hold.get(), d);
hold.release();
...
}

that way it is clear for the reader that you are giving the deleter a
pointer already owned by hold. p should not be used after the first
line regardless of move or no move.

/ Jonas

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]

Back to top
Howard Hinnant
Guest





PostPosted: Sun Sep 04, 2005 8:33 pm    Post subject: Re: unique_ptr Reply with quote

In article <1125782448.174120.58820 (AT) g14g2000cwa (DOT) googlegroups.com>,
"Jonas Persson" <l.j.persson (AT) gmail (DOT) com> wrote:

Quote:
I dont think it would sprinkle the code with move.
The only(?) places where you assign it lvaues is from a local variable:

int* i = new int:
unique_ptr<int> ip(move(i));

which is never necessary and not to be recomended, and from prameters:

void foo(int *i) {
unique_ptr<int> ip(move(i));
}

in which case the function resumes ownership of a pointer parameter.
That should never happen except for smart pointer ctors.

<nit> There are non-smart-pointer classes that may take ownership of
pointers (e.g. strstream). But even so, your point is well taken.

Quote:
The correct
signature of such a function should be: void foo(unique_ptr<int> i).

The major reason for unsafe code is memory owned by a naked pointer.
Making passing lvalues less convenient than rvalues makes using naked
pointers less convenient.

In your example, it is not nesessary to use move after the first one
anyway. Probably get() is a better choise:

template<class T
template shared_ptr : ptr_(p)
{
Metrowerks::move_ptr<Y, D&> hold(move(p), d);
s_ = new detail::shared_ptr_deleter<Y, D>(hold.get(), d);
hold.release();
...
}

that way it is clear for the reader that you are giving the deleter a
pointer already owned by hold. p should not be used after the first
line regardless of move or no move.

You make some good points. While reading your post, and re-reading your
first post, a few more things have occurred to me:

1. What if the unique_ptr<T>(T* ...) constructors looked as you suggest:

Quote:
explicit unique_ptr(T*&& p);

AND, said constructors zero'd p? I.e.:

void foo(T* p)
{
std::unique_ptr<T> up(std::move(p));
assert(p == 0);
}

This would actually put teeth into what it means to move from a built-in
pointer type. The ownership of the memory really is transferred away
from the built-in pointer.

2. If thought 1 (zeroing the input pointer) is reasonable, does it
still make sense to restrict the input parameter to an rvalue. The
constructors are already marked explicit, so the transfer of memory
ownership is never implicit. Do we need both the words "unique_ptr" and
"move" in the statement to call this fact out, or is "unique_ptr" itself
enough?

void foo(T* p)
{
std::unique_ptr<T> up(p); // sufficiently explicit?
assert(p == 0);
std::unique_ptr<T> up2(p); // up2 is null
}

3. This issue isn't specific to unique_ptr. For any smart pointer for
which the following would be bad:

T* p = ...
smart_pointer<T> p1(p);
smart_pointer<T> p2(p);

this same issues exists. This includes shared_ptr and most any other
smart pointer I can come up with. This gives us vastly more experience
to guide us. For example it is an interesting exercise to go through
the excellent "shared_ptr techniques" page to see how these proposed
changes might effect these examples:

http://www.boost.org/libs/smart_ptr/sp_techniques.html

Would we be willing to have all standard smart pointers behave in the
same way with respect to how they accept raw pointers (be it rvalue
only, zeroing, whatever)?

-Howard

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Vladimir Marko
Guest





PostPosted: Mon Sep 05, 2005 10:42 pm    Post subject: Re: unique_ptr Reply with quote

Howard Hinnant wrote:
[...]
Quote:
1. What if the unique_ptr<T>(T* ...) constructors looked as you suggest:

explicit unique_ptr(T*&& p);

AND, said constructors zero'd p? I.e.:

void foo(T* p)
{
std::unique_ptr<T> up(std::move(p));
assert(p == 0);
}

This would actually put teeth into what it means to move from a built-in
pointer type. The ownership of the memory really is transferred away
from the built-in pointer.

It would be great in debug mode. In release mode I would like the
compiler to optimize away the zeroing statement. ("You don't pay for
what you don't need.")

[...]
Quote:
3. This issue isn't specific to unique_ptr. For any smart pointer for
which the following would be bad:

T* p = ...
smart_pointer<T> p1(p);
smart_pointer<T> p2(p);

Well, this is "almost good" for intrusive_ptr -- it should work, but it
provides a bad example for those who don't undestand what's going on
under the hood.

[...]
Quote:
Would we be willing to have all standard smart pointers behave in the
same way with respect to how they accept raw pointers (be it rvalue
only, zeroing, whatever)?

That would be good for new smart pointers. But for the old ones I'd go
for the backward compatibility.

Vladimir Marko

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
thorsten.ottosen@dezide.c
Guest





PostPosted: Mon Sep 05, 2005 10:44 pm    Post subject: Re: unique_ptr Reply with quote


Howard Hinnant wrote:
Quote:
In article <1125603471.570361.32720 (AT) o13g2000cwo (DOT) googlegroups.com>,
"l.j.persson (AT) gmail (DOT) com" <l.j.persson (AT) gmail (DOT) com> wrote:

Hello,
I was just reading the excellent proposal on unique_ptr
([url]http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html)[/url].
But I have a question on the constructor: why doesn't it take a T*&&
parameter?

explicit unique_ptr(T*&& p);

If I understand rvalues correctly that would mean:

unique_ptr<int> ui(new int); // Ok
int* pi = new int;
unique_ptr<int> ui(pi); // Not Ok

[snip]

Quote:
That's a very interesting suggestion!

I must admit that I'm waffling on it at the moment. At first I thought
it sounded great, and I prototyped it right up. But then I used that
prototype to start looking at some of my use cases, and my enthusiasm is
waning.


While we're discussion potential correctness issues, then
we should probably consider adding functions like

template<class T, class U>
unique_ptr<T> make_unique_ptr( U&& );

[etc for X arguments].

The motivating example would be

foo( make_unique_ptr<T>( "foo" ), make_unique_ptr<T>( "bar" ) );

which is exception-safe whereas

foo( unique_ptr<T>( new T("foo") ), unique_ptr<T>( new T("bar") ) );

is not.

Similar stuff has been discussed for shared_ptr.

br

-Thorsten

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Jonas Persson
Guest





PostPosted: Mon Sep 05, 2005 10:44 pm    Post subject: Re: unique_ptr Reply with quote

Quote:
1. What if the unique_ptr<T>(T* ...) constructors looked as you suggest:

explicit unique_ptr(T*&& p);

AND, said constructors zero'd p? I.e.:

void foo(T* p)
{
std::unique_ptr<T> up(std::move(p));
assert(p == 0);

}

This would actually put teeth into what it means to move from a built-in
pointer type. The ownership of the memory really is transferred away
from the built-in pointer.

2. If thought 1 (zeroing the input pointer) is reasonable, does it
still make sense to restrict the input parameter to an rvalue. The
constructors are already marked explicit, so the transfer of memory
ownership is never implicit. Do we need both the words "unique_ptr" and
"move" in the statement to call this fact out, or is "unique_ptr" itself
enough?

void foo(T* p)
{
std::unique_ptr<T> up(p); // sufficiently explicit?
assert(p == 0);
std::unique_ptr<T> up2(p); // up2 is null

}

That would be a nice feature, at least in debug builds. Null

dereferences is easier to track down than double deletes..
But it is a runtime check for something that could be checked at
compile time, so I see it as a complement rather than a replacement.

I would expect a high quality compiler or lint tool to emit a warning
if I reused a std::moved lvalue.
Probably they could identify unique_ptr and some other smart pointers
too, but the less special cases the better.

Also, there is this issue with junior programmers. They need simple
rules.
"Do not use a variable after it has been used in std::move" is such a
nice simple rule.

Quote:
3. This issue isn't specific to unique_ptr. For any smart pointer for
which the following would be bad:

T* p = ...
smart_pointer<T> p1(p);
smart_pointer<T> p2(p);

this same issues exists. This includes shared_ptr and most any other
smart pointer I can come up with. This gives us vastly more experience
to guide us. For example it is an interesting exercise to go through
the excellent "shared_ptr techniques" page to see how these proposed
changes might effect these examples:

http://www.boost.org/libs/smart_ptr/sp_techniques.html

I've skimmed through this, and there are som small changes but nothing

that would prevent any of the techniques. I think that making this
trickery a little more verbose is a small price to pay for safety in
the more common cases.

Quote:
Would we be willing to have all standard smart pointers behave in the
same way with respect to how they accept raw pointers (be it rvalue
only, zeroing, whatever)?

Yes, I think this is how they should all work.


/ Jonas

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Larry Evans
Guest





PostPosted: Wed Sep 21, 2005 6:22 pm    Post subject: Re: unique_ptr Reply with quote

On 09/05/2005 05:44 PM, [email]thorsten.ottosen (AT) dezide (DOT) com[/email] wrote:
[snip]
Quote:
While we're discussion potential correctness issues, then
we should probably consider adding functions like

template<class T, class U
unique_ptr
[etc for X arguments].

The motivating example would be

foo( make_unique_ptr<T>( "foo" ), make_unique_ptr<T>( "bar" ) );

which is exception-safe whereas

foo( unique_ptr<T>( new T("foo") ), unique_ptr<T>( new T("bar") ) );

is not.

Similar stuff has been discussed for shared_ptr.


Is this similar stuff referring to the part of:

http://lists.boost.org/Archives/boost/2003/01/43503.php

about auto_ptr_new<T>? IOW, is David's auto_ptr_new your
make_unique_ptr?

TIA.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]


Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language, library and standards All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.