Topic: Proposal: add auto_array
Author: thedl0-usenet1@yahoo.com (Daryle Walker)
Date: Sat, 12 Mar 2005 03:32:23 GMT Raw View
The standard defines a "smart" pointer class to help the managing of
dynamically allocated single objects. Many have wished a similar
solution for arrayed objects. Full-blown smart pointer types have been
proposed, but we could also define a focused solution for only this
problem. Such a solution shouldn't be forced into a funky
specialization of auto_ptr (e.g. std::auto_ptr<T[]>). Such solutions
are more cute than useful, and inappropriately fuse types with different
semantics together. (I'm posting a separate message to fix an existing
example of that tragic mistake: hiding a bit-vector as
std::vector<bool>.)
1. In section 20.4 [lib.memory], for the "Header <memory> synopsis,"
change the last section of declarations to:
//==========================================
// 20.4.5, simply-managed pointers:
template<typename X> class auto_ptr;
template<typename Y> class auto_array;
//==========================================
2. Shift the current section 20.4.5 [lib.auto.ptr] into a section
20.4.5.1. (And shift any inner numbers appropriately.)
3. Add a new header for section 20.4.5 as:
//==========================================
20.4.5 Simply-managed pointers [lib.auto.pointers]
//==========================================
4. Add a new section 20.4.5.X [lib.auto.array], where "X" is a
sub-sub-sub-section number to be determined, "2" most likely. It
defines the array analog of auto_ptr as:
//==========================================
20.4.5.X Class template auto_array [lib.auto.array]
Template auto_array stores a pointer to an array segment obtained via
new[] and deletes that segment when it itself is destroyed (such as when
leaving block scope 6.7).
Nested structure auto_array_ref holds a reference to an auto_array. It
is used by the auto_array conversions to allow auto_array objects to be
passed to and returned from functions.
#include <cstddef> // for size_t
namespace std {
template<typename Y> class auto_array {
struct auto_array_ref {};
public:
typedef Y element_type;
// 20.4.5.X.1 construct/copy/destroy:
explicit auto_array( Y *a = 0 ) throw();
auto_array( auto_array & ) throw();
auto_array & operator =( auto_array & ) throw();
auto_array & operator =( auto_array_ref r ) throw();
~auto_array() throw();
// 20.4.5.X.2 members:
Y & operator []( size_t i ) const throw();
Y * get() const throw();
Y * release() throw();
void reset( Y *a = 0 ) throw();
// 20.4.5.X.3 conversions:
auto_array( auto_array_ref ) throw();
operator auto_array_ref() throw();
};
}
The auto_array provides semantics of strict ownership. An auto_array
owns the segment it holds a pointer to. Copying an auto_array copies
the pointer and transfers ownership to the destination. If more than
one auto_array owns the same segment at the same time the behavior of
the program is undefined. [Note: The uses of auto_array include
providing temporary exception-safety for dynamically allocated memory,
passing ownership of dynamically allocated memory to a function, and
returning dynamically allocated memory from a function. auto_array does
not meet the CopyConstructible and Assignable requirements for Standard
Library container elements and thus instantiating a Standard Library
container with an auto_array results in undefined behavior. --end note]
20.4.5.X.1 auto_array constructors [lib.auto.array.cons]
explicit auto_array( Y *a = 0 ) throw();
Postconditions: *this holds the pointer a.
auto_array( auto_array &a ) throw();
Effects: Calls a.release().
Postconditions: *this holds the pointer returned from a.release().
auto_array & operator =( auto_array &a ) throw();
Requires: The expression delete [] get() is well formed.
Effects: reset( a.release() ).
Returns: *this.
~auto_array() throw();
Requires: The expression delete [] get() is well formed.
Effects: delete [] get().
20.4.5.X.2 auto_array members [lib.auto.array.members]
Y & operator []( size_t i ) const throw();
Requires: get() != 0 and get()[i] is well formed.
Returns: get()[ i ]
Y * get() const throw();
Returns: The pointer *this holds.
Y * release() throw();
Returns: get()
Postcondition: *this holds the null pointer.
void reset( Y *a = 0 ) throw();
Effects: If get() != a then delete [] get().
Postconditions: *this holds the pointer a.
20.4.5.X.3 auto_array conversions [lib.auto.array.conv]
auto_array( auto_array_ref r ) throw();
Effects: Calls a.release() for the auto_array a that r holds.
Postconditions: *this holds the pointer returned from release().
operator auto_array_ref() throw();
Returns: An auto_array_ref that holds *this.
auto_array & operator =( auto_array_ref r ) throw()
Effects: Calls reset( a.release() ) for the auto_array a that r holds a
reference to.
Returns: *this
//==========================================
(The text is a rip-off of the existing section 20.4.5, except that cross
conversion is banned. That allows the puesdo-reference structure to be
moved within the main class template. It can be moved out again if it
can't work as a private inner type.)
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ 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 ]
Author: Howard Hinnant <hinnant@metrowerks.com>
Date: Sun, 13 Mar 2005 13:33:21 CST Raw View
In article <1gt9o81.4sbik4yxtzwgN%thedl0-usenet1@yahoo.com>,
thedl0-usenet1@yahoo.com (Daryle Walker) wrote:
> The standard defines a "smart" pointer class to help the managing of
> dynamically allocated single objects. Many have wished a similar
> solution for arrayed objects. Full-blown smart pointer types have been
> proposed, but we could also define a focused solution for only this
> problem. Such a solution shouldn't be forced into a funky
> specialization of auto_ptr (e.g. std::auto_ptr<T[]>). Such solutions
> are more cute than useful, and inappropriately fuse types with different
> semantics together. (I'm posting a separate message to fix an existing
> example of that tragic mistake: hiding a bit-vector as
> std::vector<bool>.)
I agree that we need a "smart pointer to array with unique ownership
semantics". The stock answer: "use vector instead" while often good
advice to newbies, is sometimes not appropriate (e.g. when array
ownership needs to be transferred to another type).
However, I have 3 concerns with the proposed auto_array, ordered from
"major concern" first, to "quibble" last:
1. A copy constructor or copy assignment operator that changes the
value of an lvalue source (like auto_ptr, and like the proposed
auto_array) is a fundamental design flaw, albeit one that is
extraordinarily hard to avoid in C++03.
When writing generic code, it is way too easy to write an assignment
that implicitly assumes that the source remains unchanged. When that
assumption is invalidated (by instantiating the generic code with
auto_ptr) a run time error is silently created. If unique-ownership
classes instead disable copy semantics and use some other syntax to
transfer ownership, this run time error is turned into a compile time
error, which is a Very Good Thing (tm).
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html
Recommends deprecating auto_ptr and replacing it with a smart pointer
with very similar functionality (search for "unique_ptr").
Jonathan Turkanis has valiantly implemented "unique_ptr" in C++03, but
spells it static_move_ptr:
http://home.comcast.net/~jturkanis/move_ptr/
Jonathan's site also gives an excellent comparison not only between
auto_ptr and (static_)move_ptr, but between "static vs dynamic" deleter
design (shared_ptr is an example of the latter). Well worth the read!
It is my sincere hope that a C++0X implementation will be considerably
simpler, so much so that it becomes trivial for average coders to give
their own classes similar (move-only) semantics. I.e. no need for
smart_ptr_ref tricks, nor enable_if tricks.
2. There are a lot of use cases for a customizable deleter. If the
deleter is part of the pointer's type, the deleter can have zero
overhead (via empty base optimization).
The customizable deleter goes far beyond the need for simply allowing
delete vs delete[]. And at the same time, the customizable deleter is
not sufficient by itself to enable a single type to serve as both
pointer to single object and pointer to array, because the interface of
the smart pointer must change when it is managing an array. (Daryle
obviously knows this as the auto_array proposal reflects this, but I'm
reiterating it for other readers).
Imho, the next generation smart pointer with unique ownership semantics
should include the ability to customize the deleter for both single
objects and arrays in such a way as to allow for a zero-overhead
implementation. The default deleters should be empty classes, and
simply do delete and delete[].
3. Yeah, smart_ptr<T[]> is cute. But that makes it easy to remember
the syntax, which is very important from a newbie-friendly and teaching
point of view.
Is it a mistake similar to vector<bool> (and I agree that vector<bool>
was a mistake)? I don't think so for one very important reason:
If we had not specialized vector<bool>, then the expression vector<bool>
would have been legal, and had different semantics. If we do not
specialize smart_ptr<T[]>, the same is not true. It is simply a compile
time failure - ill formed code. Thus the smart_ptr<T[]> specialization
is simply another name for smart_array<T>. Nothing more, nothing less.
Therefore it comes down to the very simple question: Which name is
better, considering only syntax:
smart_ptr<T[]>
smart_array<T>
Comparisons with vector<bool> are simply fud, because no valid semantics
are being displaced.
-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 ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Mon, 14 Mar 2005 02:58:47 GMT Raw View
"Howard Hinnant" <hinnant@metrowerks.com> wrote in message
news:hinnant-3CD037.13155013032005@syrcnyrdrs-01-ge0.nyroc.rr.com...
| Therefore it comes down to the very simple question: Which name is
| better, considering only syntax:
|
| smart_ptr<T[]>
| smart_array<T>
well, as I have said before, I think it could be a mistake to not store the
size of the array in smart_array<T> (or whatever name we choose).
It should IMO store the size and offer begin()/end()/data() member functions.
The reason for not soing this have been that the smart pointer was a low-level
device
and it was easier to just make a new smart-pointer on top of the zero overhead
design. IMO, it should be just as easy to wrap smart_ptr<T> into a
smart_array<T>
if that is needed.
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 ]
Author: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Mon, 14 Mar 2005 02:58:44 GMT Raw View
Howard Hinnant wrote:
> Jonathan Turkanis has valiantly implemented "unique_ptr" in C++03, but
> spells it static_move_ptr:
>
> http://home.comcast.net/~jturkanis/move_ptr/
>
> Jonathan's site also gives an excellent comparison not only between
> auto_ptr and (static_)move_ptr, but between "static vs dynamic"
> deleter design (shared_ptr is an example of the latter). Well worth
> the read!
Thanks, Howard.
move_ptr is also available here, with a faster server:
http://www.kangaroologic.com/move_ptr/
Daryle Walker wrote:
> The standard defines a "smart" pointer class to help the managing of
> dynamically allocated single objects. Many have wished a similar
> solution for arrayed objects. Full-blown smart pointer types have
> been proposed, but we could also define a focused solution for only
> this problem. Such a solution shouldn't be forced into a funky
> specialization of auto_ptr (e.g. std::auto_ptr<T[]>).
Dave Held's and Andrei Alexandrescu's policy-based smart pointer
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1681.pdf, based on
Andrei's SmartPtr from Loki) can give you auto_array and unique_array with or
without Howard's special syntax for arrays. The reference implementation should
be coming up for review at Boost within the next few weeks. (Perhaps this is
what you mean by "full-blown smart pointer types.")
Jonathan
---
[ 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 ]
Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website For Email)")
Date: Mon, 14 Mar 2005 19:05:11 GMT Raw View
Howard Hinnant wrote:
> Jonathan Turkanis has valiantly implemented "unique_ptr" in C++03, but
> spells it static_move_ptr:
>
> http://home.comcast.net/~jturkanis/move_ptr/
>
> Jonathan's site also gives an excellent comparison not only between
> auto_ptr and (static_)move_ptr, but between "static vs dynamic" deleter
> design (shared_ptr is an example of the latter). Well worth the read!
I've looked at that URL and the move pointers support the constructor:
static_move_ptr(const static_move_ptr& p)
: impl_(p.get(), p.get_deleter())
{
const_cast<static_move_ptr&>(p).release();
}
Even without looking at the rest of the code, doesn't that means that
the static_move_ptr will take lvalues of type const static_move_ptr?
const static_move_ptr<int> p1(new int);
static_move_ptr<int> p2(p1);
How does the code disallow this initialization?
Andrei
---
[ 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 ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Sun, 20 Mar 2005 05:56:17 GMT Raw View
"Daryle Walker" <thedl0-usenet1@yahoo.com> wrote in message
news:1gtgzf1.wnozya1iboplsN%thedl0-usenet1@yahoo.com...
| "Thorsten Ottosen" <nesotto@cs.auc.dk> wrote:
| I originally thought of adding a length constructor to auto array:
| that either. You have to keep track of the length yourself (and, like
| Howard Hinnant said in a different reply, your personal tracking scheme
| may be superior).
and as I pointed out, it was easier to adapt unique_ptr<T> for arrays
without size than to adapt unique_ptr<T[]> to have a size.
-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 ]
Author: hinnant@metrowerks.com (Howard Hinnant)
Date: Mon, 14 Mar 2005 19:05:21 GMT Raw View
In article <4234aa4d$0$29281$14726298@news.sunsite.dk>,
nesotto@cs.auc.dk ("Thorsten Ottosen") wrote:
> "Howard Hinnant" <hinnant@metrowerks.com> wrote in message
> news:hinnant-3CD037.13155013032005@syrcnyrdrs-01-ge0.nyroc.rr.com...
>
> | Therefore it comes down to the very simple question: Which name is
> | better, considering only syntax:
> |
> | smart_ptr<T[]>
> | smart_array<T>
>
> well, as I have said before, I think it could be a mistake to not store the
> size of the array in smart_array<T> (or whatever name we choose).
> It should IMO store the size and offer begin()/end()/data() member functions.
>
> The reason for not soing this have been that the smart pointer was a
> low-level
> device
> and it was easier to just make a new smart-pointer on top of the zero
> overhead
> design. IMO, it should be just as easy to wrap smart_ptr<T> into a
> smart_array<T>
> if that is needed.
Another reason for not storing the size here is that it may be stored
elsewhere more efficiently. For example:
struct MyObject
{
public:
..
private:
vector<unique_ptr<int[]> > data_;
unsigned data_size_;
};
Here, for whatever reasons, the coder wants a vector of arrays of int,
with each array of int being the same size (data_size_). If
unique_ptr<int[]> stores its own size, then we have needlessly added
O(N) storage to this design.
However if unique_ptr<int[]> does not store its size, then it is very
easy for clients to supply a custom deleter which does bundle the size
with the smart pointer if that is what they want:
struct array_size
{
const size_t size;
explicit array_size(size_t s) : size(s) {}
template <class T>
void operator() (T* ptr) {delete [] ptr;}
};
..
size_t s = 3;
unique_ptr<int[], array_size> p(new int[s], array_size(s));
size_t n = p.get_deleter().size; // n = 3
Iow, if we add it, clients can't take it away. If we don't add it,
clients can add it later. The latter gives clients more options.
-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 ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Mon, 14 Mar 2005 20:17:01 GMT Raw View
SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website For Email)") writes:
> Howard Hinnant wrote:
>> Jonathan Turkanis has valiantly implemented "unique_ptr" in C++03,
>> but spells it static_move_ptr:
>> http://home.comcast.net/~jturkanis/move_ptr/
>> Jonathan's site also gives an excellent comparison not only between
>> auto_ptr and (static_)move_ptr, but between "static vs dynamic"
>> deleter design (shared_ptr is an example of the latter). Well worth
>> the read!
>
> I've looked at that URL and the move pointers support the constructor:
>
> static_move_ptr(const static_move_ptr& p)
> : impl_(p.get(), p.get_deleter())
> {
> const_cast<static_move_ptr&>(p).release();
> }
>
> Even without looking at the rest of the code, doesn't that means that
> the static_move_ptr will take lvalues of type const static_move_ptr?
>
> const static_move_ptr<int> p1(new int);
> static_move_ptr<int> p2(p1);
>
> How does the code disallow this initialization?
IIUC the trick is in causing an illegal instantiation of another ctor
at the same time. This is sorta the opposite of SFINAE:
template<typename Ptr> struct cant_move_from_const;
template<typename TT, typename DD>
struct cant_move_from_const< const static_move_ptr<TT, DD> > {
typedef typename static_move_ptr<TT, DD>::error type;
};
template<typename Ptr>
static_move_ptr(Ptr&, typename cant_move_from_const<Ptr>::type = 0);
static_move_ptr has no ::error member.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ 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 ]
Author: "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail@moderncppdesign.com>
Date: Mon, 14 Mar 2005 17:19:44 CST Raw View
David Abrahams wrote:
> IIUC the trick is in causing an illegal instantiation of another ctor
> at the same time. This is sorta the opposite of SFINAE:
>
> template<typename Ptr> struct cant_move_from_const;
>
> template<typename TT, typename DD>
> struct cant_move_from_const< const static_move_ptr<TT, DD> > {
> typedef typename static_move_ptr<TT, DD>::error type;
> };
>
> template<typename Ptr>
> static_move_ptr(Ptr&, typename cant_move_from_const<Ptr>::type = 0);
>
> static_move_ptr has no ::error member.
Ah, thanks Dave. Somehow I was thinking that the standard copy ctor
overwhelms everything.
That's a nice trick, because it also keeps the compiler happy in all
other cases - the standard copy ctor is there. Cool!
Andrei
---
[ 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 ]
Author: "Thorsten Ottosen" <nesotto@cs.auc.dk>
Date: Mon, 14 Mar 2005 17:19:46 CST Raw View
"Howard Hinnant" <hinnant@metrowerks.com> wrote in message
news:hinnant-6BE2CE.08482914032005@syrcnyrdrs-03-ge0.nyroc.rr.com...
| In article <4234aa4d$0$29281$14726298@news.sunsite.dk>,
| nesotto@cs.auc.dk ("Thorsten Ottosen") wrote:
| > well, as I have said before, I think it could be a mistake to not store
the
| > size of the array in smart_array<T> (or whatever name we choose).
| > It should IMO store the size and offer begin()/end()/data() member
functions.
| Another reason for not storing the size here is that it may be stored
| elsewhere more efficiently. For example:
|
| struct MyObject
| {
| public:
| ..
| private:
| vector<unique_ptr<int[]> > data_;
| unsigned data_size_;
| };
|
| Here, for whatever reasons, the coder wants a vector of arrays of int,
| with each array of int being the same size (data_size_). If
| unique_ptr<int[]> stores its own size, then we have needlessly added
| O(N) storage to this design.
|
| However if unique_ptr<int[]> does not store its size, then it is very
| easy for clients to supply a custom deleter which does bundle the size
| with the smart pointer if that is what they want:
|
| struct array_size
| {
| const size_t size;
| explicit array_size(size_t s) : size(s) {}
| template <class T>
| void operator() (T* ptr) {delete [] ptr;}
| };
|
| ..
|
| size_t s = 3;
| unique_ptr<int[], array_size> p(new int[s], array_size(s));
| size_t n = p.get_deleter().size; // n = 3
this is pretty ugly IMO. I think good programmers can easier
do what they need than novices/intermediate. Therefore the default
should benefit the novice/intermediate.
I should be fairly easy to get the no-overhead version:
struct MyObject
{
public:
..
private:
vector<unique_ptr<int, array_deleter> > data_;
unsigned data_size_;
};
then use
data_.push_back( unique_ptr<int>( new int[ data_size_ ] ) );
data_[0]->get()[5];
IMO, that is a lot easier than the solution you wanted to give the
inexperienced. :-)
br
-Thorsten
BTW: I'm not keen about that operator*() and operator->() is not overloaded on
const. This makes it much
harder to write const-correct programs; the fact that we hold T in a
unique_ptr<T> has
nothing to do with the mutability/constness of T's interface; we just use a
pointer because we must.
---
[ 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 ]
Author: thedl0-usenet1@yahoo.com (Daryle Walker)
Date: Wed, 16 Mar 2005 14:02:36 CST Raw View
"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote:
> "Howard Hinnant" <hinnant@metrowerks.com> wrote in message
> news:hinnant-3CD037.13155013032005@syrcnyrdrs-01-ge0.nyroc.rr.com...
>
> | Therefore it comes down to the very simple question: Which name is
> | better, considering only syntax:
> |
> | smart_ptr<T[]>
> | smart_array<T>
>
> well, as I have said before, I think it could be a mistake to not store
> the size of the array in smart_array<T> (or whatever name we choose). It
> should IMO store the size and offer begin()/end()/data() member functions.
>
> The reason for not soing this have been that the smart pointer was a
> low-level device and it was easier to just make a new smart-pointer on top
> of the zero overhead design. IMO, it should be just as easy to wrap
> smart_ptr<T> into a smart_array<T> if that is needed.
I originally thought of adding a length constructor to auto array:
//==================================================
template <typename T>
class auto_array
{
//...
explicit auto_array( size_t len ) : p_( new T[len] ) {}
//...
};
//==================================================
But I wasn't going to store the size anywhere, so I thought it would be
a bad impression if it looked like that I did. I want the class to be
as no-frills and close to auto_ptr as possible, so it has one main
constructor, the one that takes a pointer. I don't store the array
segment size because pointer-accessed array segements don't give you
that either. You have to keep track of the length yourself (and, like
Howard Hinnant said in a different reply, your personal tracking scheme
may be superior).
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ 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 ]
Author: thedl0-usenet1@yahoo.com (Daryle Walker)
Date: Wed, 16 Mar 2005 20:02:05 GMT Raw View
Howard Hinnant <hinnant@metrowerks.com> wrote:
> In article <1gt9o81.4sbik4yxtzwgN%thedl0-usenet1@yahoo.com>,
> thedl0-usenet1@yahoo.com (Daryle Walker) wrote:
>
> > The standard defines a "smart" pointer class to help the managing of
> > dynamically allocated single objects. Many have wished a similar
> > solution for arrayed objects. Full-blown smart pointer types have been
> > proposed, but we could also define a focused solution for only this
> > problem. Such a solution shouldn't be forced into a funky
> > specialization of auto_ptr (e.g. std::auto_ptr<T[]>). Such solutions
> > are more cute than useful, and inappropriately fuse types with different
> > semantics together. (I'm posting a separate message to fix an existing
> > example of that tragic mistake: hiding a bit-vector as
> > std::vector<bool>.)
>
> I agree that we need a "smart pointer to array with unique ownership
> semantics". The stock answer: "use vector instead" while often good
> advice to newbies, is sometimes not appropriate (e.g. when array
> ownership needs to be transferred to another type).
>
> However, I have 3 concerns with the proposed auto_array, ordered from
> "major concern" first, to "quibble" last:
>
> 1. A copy constructor or copy assignment operator that changes the
> value of an lvalue source (like auto_ptr, and like the proposed
> auto_array) is a fundamental design flaw, albeit one that is
> extraordinarily hard to avoid in C++03.
Actually, I don't mind the current syntax.
> When writing generic code, it is way too easy to write an assignment
> that implicitly assumes that the source remains unchanged. When that
> assumption is invalidated (by instantiating the generic code with
> auto_ptr) a run time error is silently created. If unique-ownership
> classes instead disable copy semantics and use some other syntax to
> transfer ownership, this run time error is turned into a compile time
> error, which is a Very Good Thing (tm).
But auto_ptr (and auto_array) already claim that they are not
copy-constructible nor assignable for the purposes of container elements
(and similar situations).
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html
>
> Recommends deprecating auto_ptr and replacing it with a smart pointer
> with very similar functionality (search for "unique_ptr").
[SNIP]
> 2. There are a lot of use cases for a customizable deleter. If the
> deleter is part of the pointer's type, the deleter can have zero
> overhead (via empty base optimization).
>
> The customizable deleter goes far beyond the need for simply allowing
> delete vs delete[]. And at the same time, the customizable deleter is
> not sufficient by itself to enable a single type to serve as both
> pointer to single object and pointer to array, because the interface of
> the smart pointer must change when it is managing an array. (Daryle
> obviously knows this as the auto_array proposal reflects this, but I'm
> reiterating it for other readers).
>
> Imho, the next generation smart pointer with unique ownership semantics
> should include the ability to customize the deleter for both single
> objects and arrays in such a way as to allow for a zero-overhead
> implementation. The default deleters should be empty classes, and
> simply do delete and delete[].
I'm not saying that we can't add "conventional" smart pointer types, but
I want to add a direct solution to the most immediate problem. I don't
want it lost as a special case in a sea of over-configured overkill.
> 3. Yeah, smart_ptr<T[]> is cute. But that makes it easy to remember
> the syntax, which is very important from a newbie-friendly and teaching
> point of view.
Giving newbies inappropriate abstractions isn't doing them any favors.
> Is it a mistake similar to vector<bool> (and I agree that vector<bool>
> was a mistake)? I don't think so for one very important reason:
>
> If we had not specialized vector<bool>, then the expression vector<bool>
> would have been legal, and had different semantics. If we do not
> specialize smart_ptr<T[]>, the same is not true. It is simply a compile
> time failure - ill formed code. Thus the smart_ptr<T[]> specialization
> is simply another name for smart_array<T>. Nothing more, nothing less.
But a specialization should have the same interface as the base case,
just an optimized implementation. A smart pointer for arrays should
have a different interface than a smart pointer for single objects. It
is very inappropriate to stuff one as a specialization of the other.
Worse, "T[]" isn't a real type outside of cute meta-programming tricks.
It can't be used conventionally (since true array types specify their
element length).
> Therefore it comes down to the very simple question: Which name is
> better, considering only syntax:
>
> smart_ptr<T[]>
> smart_array<T>
I think the second one. I could think of the first one as a smart
pointer with single-object semantics (with * and ->, but no []) where
the single object just happens to be an array. (The only reason why
that can't work now is due to C++, and C, treating arrays as half-@$$
types. Should your interpretation work if arrays acted as if they were
wrapped in a struct?)
> Comparisons with vector<bool> are simply fud, because no valid semantics
> are being displaced.
But we shouldn't invent semantics for constructs that were never meant
to be proper.
--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net
---
[ 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 ]