 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
l.j.persson@gmail.com Guest
|
Posted: Fri Sep 02, 2005 5:11 am Post subject: unique_ptr |
|
|
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
|
Posted: Sat Sep 03, 2005 5:23 am Post subject: Re: unique_ptr |
|
|
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
|
Posted: Sun Sep 04, 2005 2:20 am Post subject: Re: unique_ptr |
|
|
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
|
Posted: Sun Sep 04, 2005 8:33 pm Post subject: Re: unique_ptr |
|
|
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
|
Posted: Mon Sep 05, 2005 10:42 pm Post subject: Re: unique_ptr |
|
|
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
|
Posted: Mon Sep 05, 2005 10:44 pm Post subject: Re: unique_ptr |
|
|
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
|
Posted: Mon Sep 05, 2005 10:44 pm Post subject: Re: unique_ptr |
|
|
| 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
|
Posted: Wed Sep 21, 2005 6:22 pm Post subject: Re: unique_ptr |
|
|
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 |
|
 |
|
|
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
|
|