Re: auto_ptr vs. boost shared_ptr
Howard Hinnant wrote:
In article <1153301658.835468.72190@m73g2000cwd.googlegroups.com>,
"kanze" <kanze@gabi-soft.fr> wrote:
[...]
unique_ptr is a small part of a larger proposal: the rvalue
reference - a language change. Included in the language
changes is (informally speaking) anywhere copy elision is
current legal, consider the source of the "copy" to be an
rvalue instead of an lvalue, thus invoking a move constructor
if it exists, in preference to a copy constructor (or just
elide the move constructor as you would have the copy
constructor).
I've downloaded a copy of the proposal. Now I just have to find
the time to read it.
So, one can return moveable but non-copyable objects from
functions as long as the source is a non-cv-qualified auto
object with the same type as the return:
std::ifstream
find_and_open_file(const std::string& filename)
{
std::ifstream the_file;
// find it and open it
return the_file; // moves, not copies out.
// This retains the invariant that only
// one ifstream object refers to the
// physical file.
}
The move will probably be elided anyway with most modern
compilers. But it is still logically a move, not a copy.
Because, of course, we definitly don't want ifstream to be
copiable. Generally, however, I hadn't paid much attention to
the proposal, because I thought it mainly concerned optimization
issues. But it looks very interesting on semantic grounds as
well, precisely for such cases where you want to be able to
"copy", but you also have the constrait that there should be
only a single object for the specific "value".
(In this particular case, it probably doesn't help that much,
because at the end of the day, what we want and need is an
istream, not an ifstream. And I don't quite see how you can
move an ifstream into an istream.)
[...]
Sounds good. (The next obvious question is: where can I
access an implementation, to try it out? Since we all know
that things which sound good on paper don't always work out
that well in practice.)
There's good news and bad news here. It has been 100%
implemented, both language and library (that's the good news),
except that std::unique_ptr is called Metrowerks::move_ptr.
The bad news is that it is only available on CodeWarrior Pro
10, Macintosh only, and I'm not even sure Freescale is still
selling it - they have exited that market. If you have it, or
can get your hands on it, it is not available by default. You
have to:
#pragma rvalue_refs on
preferably in a prefix file.
Russell Yanofsky reported that he implemented it in gcc 4.0
about a year ago.
http://groups.google.com/group/comp.std.c++/browse_frm/thread/64bf775bdf0
69dad/3b905848b46253d5?lnk=st&q=rvalue+reference+group%3Acomp.std.c%2B%2B
&rnum=1&hl=en#3b905848b46253d5
Unfortunately his link is no longer working, and as far as I
know, this work never made it into gcc (I'd love to see it
there).
Well, at least some people have used it. That's a good start.
[...]
Such containers of movable but non-copyable types will
also be able to be manipulated with many std::algorithms
(which will use swap/move instead of copy) such as sort,
push/pop_heap, partition, rotate, remove_if, etc. If an
attempt is made to use such a sequence with an algorithm
that requires copy semantics, a compile-time error will
result.
Note that ownership transfer requires a non-const
unique_ptr source. If you want to prohibit ownership
transfer from a unique_ptr, (like scoped_ptr), make it
const.
Which isn't always an option, e.g. if I want my pimpl to
support assignment.
This might look like:
struct impl;
class Pimpl
{
private:
std::unique_ptr<impl> p_;
public:
Pimpl();
// ...
Pimpl& operator=(const Pimpl& p);
};
int main()
{
Pimpl p1, p2;
p2 = p1;
}
// --firewall--
struct impl
{
int data_;
};
Pimpl::Pimpl()
: p_(new impl)
{
}
Pimpl&
Pimpl::operator=(const Pimpl& p)
{
if (this != &p)
*p_ = *p.p_;
That's not what I want. Normally, the implementation object
itself will not be assignable (because I don't know how to
assign it in an exception safe manner). The motivation for the
pimpl idiom isn't just to break compile time dependencies, it is
also to provide the strong guarantee in a simple way:
Pimpl tmp( p ) ;
std::swap( tmp.p_, p_ ) ;
with raw pointers.
return *this;
}
In the end, it sounds like a good replacement for auto_ptr.
I think I'd rather prefer to have a scoped_ptr along side of
it, if for no other reason that it seems clearer to have two
distinctly named types for two different target semantics.
Upon further investigation, boost::scoped_ptr isn't really
guaranteeing you a scoped lifetime either:
scoped_ptr<T> global;
void foo()
{
scoped_ptr<T> p(/*...*/);
// use *p
swap(p, global);
}
Agreed. But it expresses the intent. Most importantly, here,
it expresses the intent that I have a different use case than
when I use the pointer as a parameter for MessageQueue::send().
Conceptually, these are two very different things, and I'd like
to have two different names for them.
There are two fixes if you want to restrict this further:
const scoped_ptr<T> p(/*...*/);
or remove swap from scoped_ptr's repertoire.
Of course and then there's really bizarre use cases if we care
to worry about them:
scoped_ptr<T>* p = new scoped_ptr<T>(/*...*/)
// use **p
Such users probably deserve whatever they get. :-)
Agreed. The consideration in my mind here is largely one of
expression of intent, rather than one of real protection. I
think the unique_ptr gives me the protection I was worried
about: accidentally forgetting to provide the copy constructor
and/or the assignment operator, and getting a compiler provided
default which did something different than what I wanted.
--
James Kanze GABI Software
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]