about boost::scoped_ptr

Z

zs0723

class A
{
struct A_impl;
private:
boost::scoped_ptr<A_impl> impl_
A(const& A);
A & operator=(const& A );
public:
A();


~A()
{
}
}

struct A::A_impl
{
...
}

A::A():impl_(new A_impl)
{

}

// my question is:
why i have to write the destructor explicitly
to let scoped_ptr delete A_impl object
although in destructor i do nothing ;
implicit generated destructor can not work
 
K

keith

// my question is:
why i have to write the destructor explicitly
to let scoped_ptr delete A_impl object
although in destructor i do nothing ;
implicit generated destructor can not work

That doesn't sound right. The destructor for the scoped_ptr should
get run regardless, no?
 
J

James Kanze

class A
{
struct A_impl;
private:
boost::scoped_ptr<A_impl> impl_
A(const& A);
A & operator=(const& A );
public:
A();

struct A::A_impl
{
...
}
A::A():impl_(new A_impl)
{
}
// my question is:
why i have to write the destructor explicitly
to let scoped_ptr delete A_impl object
although in destructor i do nothing ;
implicit generated destructor can not work

I'm not sure you do; IIRC, boost::shared_ptr uses some special
tricks so that it is sufficient that the class have a complete
type when its constructor is instantiated (here, in the
constructor of A::A(). For most smart pointers, however, the
type must be complete when the destructor is instantiated as
well, and the standard requires that the type be complete just
to declare an std::auto_ptr.
 
B

Barry

class A
{
struct A_impl;
private:
boost::scoped_ptr<A_impl> impl_
A(const& A);
A & operator=(const& A );
public:
A();

~A()
{
}

}

struct A::A_impl
{
...

}

A::A():impl_(new A_impl)
{

}

// my question is:
why i have to write the destructor explicitly
to let scoped_ptr delete A_impl object
although in destructor i do nothing ;
implicit generated destructor can not work

Maybe this is a workaround for your specific compiler. (g++??)
IIRC, VC8 doesn't require you to do so.

boost::scope_ptr is not suitable for Pimpl idom,
as the template parameter (A_impl) should be complete at the point
where you instantiate boost::scope_ptr (when you declare impl_).
(there boost::scope_ptr::~scope_ptr *delete* the pointer,
delete needs the type to be complete)
 
K

keith

boost::scope_ptr is not suitable for Pimpl idom,
as the template parameter (A_impl) should be complete at the point
where you instantiate boost::scope_ptr (when you declare impl_).
(there boost::scope_ptr::~scope_ptr *delete* the pointer,
delete needs the type to be complete)

Erm... actually that appears not to be the case. See, for example, in
the documentation at http://www.boost.org/libs/smart_ptr/scoped_ptr.htm:

<quote>
Handle/Body Idiom

One common usage of scoped_ptr is to implement a handle/body (also
called pimpl) idiom which avoids exposing the body (implementation) in
the header file.

The scoped_ptr_example_test.cpp sample program includes a header file,
scoped_ptr_example.hpp, which uses a scoped_ptr<> to an incomplete
type to hide the implementation. The instantiation of member functions
which require a complete type occurs in the scoped_ptr_example.cpp
implementation file.
</quote>
 
Z

zs0723

Maybe this is a workaround for your specific compiler. (g++??)
IIRC, VC8 doesn't require you to do so.

boost::scope_ptr is not suitable for Pimpl idom,
as the template parameter (A_impl) should be complete at the point
where you instantiate boost::scope_ptr (when you declare impl_).
(there boost::scope_ptr::~scope_ptr *delete* the pointer,
delete needs the type to be complete)- Òþ²Ø±»ÒýÓÃÎÄ×Ö -

- ÏÔʾÒýÓõÄÎÄ×Ö -

my compiler is vc7, it does not work if i don't write destructor
A::~A£¨£© for class A explicitly.
 
J

James Kanze

Maybe this is a workaround for your specific compiler. (g++??)
IIRC, VC8 doesn't require you to do so.

Which doesn't really mean anything. If the destructor were
defined out of line, at a point where A::A_impl were already
defined, the code would still be illegal with std::auto_ptr,
instead of boost::scoped_ptr, but it would compile and work with
most compilers anyway.
boost::scope_ptr is not suitable for Pimpl idom, as the
template parameter (A_impl) should be complete at the point
where you instantiate boost::scope_ptr (when you declare
impl_). (there boost::scope_ptr::~scope_ptr *delete* the
pointer, delete needs the type to be complete)

The issues here are:

-- the type A_impl is incomplete when the class
boost::scoped_ptr< A_impl > is instantiated,

-- the type A_impl is incomplete when the destructor of
boost::scoped_ptr< A_impl > is instantiated, but

-- the type A_impl is complete when the construtor of
boost::scoped_ptr< A_impl >

The common requirements for the Boost smart pointers say:

These smart pointer class templates have a template
parameter, T, which specifies the type of the object
pointed to by the smart pointer. The behavior of the
smart pointer templates is undefined if the destructor
or operator delete for objects of type T throw
exceptions.

T may be an incomplete type at the point of smart
pointer declaration. Unless otherwise specified, it is
required that T be a complete type at points of smart
pointer instantiation. Implementations are required to
diagnose (treat as an error) all violations of this
requirement, including deletion of an incomplete type.
See the description of the checked_delete function
template.

Note that shared_ptr does not have this restriction, as
most of its member functions do not require T to be a
complete type.

The second paragraph isn't as clear as one might like, but given
the context, I'm pretty sure that what it is trying to say is
that T must be a complete type at the points where any smart
pointer member functions are instantiated. The destructor of
scoped_ptr< A_impl > will be instantiated in the destructor of
class A, so A_impl must be a complete type when this destructor
is defined. Practically speaking, this means that scoped_ptr
will work if (and only if) the destructor is out of line. (Note
that if you didn't use the smart pointer, but explicitly invoked
delete in the destructor, you'd have to define it some place
where A_impl was complete, and so out of line, as well.)

Note that boost::scoped_ptr doesn't have this restriction. For
boost::scoped_ptr:

The class template is parameterized on T, the type of
the object pointed to. shared_ptr and most of its member
functions place no requirements on T; it is allowed to
be an incomplete type, or void. Member functions that
do place additional requirements (constructors, reset)
are explicitly documented below.

(Also note that unlike a lot of commonly available libraries,
Boost actually has some usable documentation.)
 
J

Juha Nieminen

Barry said:
boost::scope_ptr is not suitable for Pimpl idom,
as the template parameter (A_impl) should be complete at the point
where you instantiate boost::scope_ptr (when you declare impl_).

I don't understand why that requirement makes it unsuitable for the
Pimpl idiom. Just because the type is incomplete at the *declaration* of
the scoped_ptr member variable, that doesn't mean that the type in
question is incomplete when this variable is *constructed* (which
happens in the constructor of the A class in this example). As long as
the type is fully declared when the constructor of A is implemented,
there should be no problem in constructing the scoped_ptr variable
correctly.
(there boost::scope_ptr::~scope_ptr *delete* the pointer,
delete needs the type to be complete)

Even if boost::scoped_ptr didn't support incomplete types (I don't
remember if it does), there's no problem as long as you implement a
destructor for the A class in a context where the type is fully
declared. The destructor of the scoped_ptr variable gets called from
this destructor of A, and if the type is complete in the context,
everything should work just fine.

(Declaring the destructor of A is not the same thing as implementing
it. The type can be incomplete at the point where the destructor of A is
declared, as long as it's not incomplete when this destructor is
implemented.)
 
J

James Kanze

I don't understand why that requirement makes it unsuitable
for the Pimpl idiom. Just because the type is incomplete at
the *declaration* of the scoped_ptr member variable, that
doesn't mean that the type in question is incomplete when this
variable is *constructed* (which happens in the constructor of
the A class in this example). As long as the type is fully
declared when the constructor of A is implemented, there
should be no problem in constructing the scoped_ptr variable
correctly.

That's not what the documentation of scoped_ptr says---I think
you're thinking of shared_ptr.
Even if boost::scoped_ptr didn't support incomplete types (I
don't remember if it does), there's no problem as long as you
implement a destructor for the A class in a context where the
type is fully declared. The destructor of the scoped_ptr
variable gets called from this destructor of A, and if the
type is complete in the context, everything should work just
fine.

It depends on the class requirements. Declaring a member
variable with the type will instantiate the class, and
std::auto_ptr requires the type to be complete in order to
instantiate the class; there's nothing you can do with it with
an incomplete type. Boosts smart pointers generally allow
instantiating the class with an incomplete type, but require the
type to be complete any time a member function (e.g. the
destructor) is instantiated. boost::shared_ptr is an exception,
and only requires the type to be complete when the constructors
or the function reset() are called.
(Declaring the destructor of A is not the same thing as
implementing it. The type can be incomplete at the point where
the destructor of A is declared, as long as it's not
incomplete when this destructor is implemented.)

It's use of the function (the destructor of scoped_ptr) which
triggers its instantiation; the function is only used
(implicitly) in the definition of the destructor of the
containing class. (If the destructor is implicitly defined, by
the compiler, it is defined in the context immediately following
the class definition.)
 
J

Juha Nieminen

James said:
That's not what the documentation of scoped_ptr says---I think
you're thinking of shared_ptr.

No, shared_ptr explicitly supports incomplete types. My point was that
even if scoped_ptr didn't, I can't think of any reason why it could
nevertheless be used with incomplete types as a class member function as
long as you implement the constructor and destructor for that class.

Simply declaring that class A has a member variable named
boost::scoped_ptr<IncompleteType> ptr; is not the same thing as
constructing that member variable. The construction of that variable
happens in the constructor of A, and as long as the type is not
incomplete in that context, I can't think of any reason why it wouldn't
work. Likewise for the destructor.

Instead of just saying "it might not work", could you please give me a
*precise reason* for it not to work? I want to know.
It depends on the class requirements. Declaring a member
variable with the type will instantiate the class, and
std::auto_ptr requires the type to be complete in order to
instantiate the class; there's nothing you can do with it with
an incomplete type.

Exactly what is it that stops it from working? As long as the type is
complete when the constructor of the class is implemented, I can't think
of any reason why it wouldn't work.

In other words, why wouldn't this work?

// A.hh
class ToBeManaged;
class A
{
std::auto_ptr<ToBeManaged> ptr;

public:
A();
~A();
};

// A.cc
#include "A.hh"
#include "ToBeManaged.hh"

A::A() {} // Implicitly calls the constructor of 'ptr'
A::~A() {} // Implicitly calls the destructor of 'ptr'

'ptr' gets constructed here, and in this context ToBeManaged has been
fully declared. Why wouldn't it work?
Likewise for the destructor.
Boosts smart pointers generally allow
instantiating the class with an incomplete type

Are you sure those smart pointers can be constructed when the type is
incomplete? Doesn't, for example, boost::shared_ptr require for the type
to be complete when it's constructed?

In other words, can you, at least in theory, do this:

class ToBeManaged;
void foo(ToBeManaged* instance)
{
boost::shared_ptr<ToBeManaged> ptr = instance; // Will this compile?
boost::shared_ptr<ToBeManaged> ptr2; // Or even this?
...
}
, but require the
type to be complete any time a member function (e.g. the
destructor) is instantiated.

Isn't it, thus, enough for the destructor of the class (which has a
smart pointer as member variable) to have an explicitly implemented
destructor (which is implemented in a context where the type being
managed is complete)?
 
J

James Kanze

No, shared_ptr explicitly supports incomplete types. My point
was that even if scoped_ptr didn't, I can't think of any
reason why it could nevertheless be used with incomplete types
as a class member function as long as you implement the
constructor and destructor for that class.

I think there is confusion among a number of different
statements here. And perhaps I misread what you were claiming.
With shared_ptr, it is sufficient that the class be complete
when the pointer is constructed; it is not necessary for it to
be complete when the class is destructed. For scoped_ptr, it is
necessary that the class be complete any time a member function
of scoped_ptr is instantiated. Basically:

Instantiation class must be complete:

shared_ptr other Boost pointers
Declaration: no no
Member functions other
than ctor, reset(): no yes
Constructor, reset(): yes yes
Simply declaring that class A has a member variable named
boost::scoped_ptr<IncompleteType> ptr; is not the same thing
as constructing that member variable. The construction of that
variable happens in the constructor of A, and as long as the
type is not incomplete in that context, I can't think of any
reason why it wouldn't work. Likewise for the destructor.

Agreed. The point I was making is that it must be complete for
the destructor as well (which is not the case with shared_ptr).
Instead of just saying "it might not work", could you please
give me a *precise reason* for it not to work? I want to know.

I don't think that that's the right question. There is only one
"precise reason" some code using a library component might not
work: it fails to meet the requirements specified by the
library.
Exactly what is it that stops it from working?

The second paragraph of section 17.4.3.6 of the standard: "In
particular, the effects are undefined in the following cases:
[...] -- if an incomplete type is used as a template argument
when instantiating a template component." Basically, this
sentence establishes a contract between you and the library
implementor; you guarantee that the type will be complete, and
because of this, the library implementor is free to count on it
being complete.
As long as the type is complete when the constructor of the
class is implemented, I can't think of any reason why it
wouldn't work.

Because the contract says it's not required to work.
In other words, why wouldn't this work?

// A.hh
class ToBeManaged;
class A
{
std::auto_ptr<ToBeManaged> ptr;

// A.cc
#include "A.hh"
#include "ToBeManaged.hh"
A::A() {} // Implicitly calls the constructor of 'ptr'
A::~A() {} // Implicitly calls the destructor of 'ptr'
'ptr' gets constructed here, and in this context ToBeManaged
has been fully declared. Why wouldn't it work?

Because the contract says that it's not required to work. (In
practice, it's likely to work in any implementation which
doesn't use concepts. But that's beside the point. The code
has undefined behavior.)
Likewise for the destructor.
Are you sure those smart pointers can be constructed when the
type is incomplete? Doesn't, for example, boost::shared_ptr
require for the type to be complete when it's constructed?

I said "instantiating the class", not instantiating the member
functions. In my table above, a column for std::auto_ptr would
have yes in every line.
In other words, can you, at least in theory, do this:
class ToBeManaged;
void foo(ToBeManaged* instance)
{
boost::shared_ptr<ToBeManaged> ptr = instance; // Will this compile?
boost::shared_ptr<ToBeManaged> ptr2; // Or even this?
...
}

Whether they will compile or not is an open question. Boost
doesn't guarantee that illegal use will fail to compile (or does
it? I seem to recall seeing something along those lines in the
specification). And the documentation seems a little bit
ambiguous with regards to the second. There is a more or less
general statement which suggests that constructors and reset()
require a complete type, but when you read the exact
specifications for the default constructor, this isn't a
requirement. Interestingly enough, the documentation of reset()
doesn't say that a complete type is required either. But both
reset() and operator=() are defined in terms of a constructor,
so the requirement is indirectly present.

(The labels in my table above are based on the generic statement
in the Boost documentation. It might be more accurate to say
that a complete type is needed when constructing with a ponter,
assignment or reset() with a pointer argument.)
Isn't it, thus, enough for the destructor of the class (which
has a smart pointer as member variable) to have an explicitly
implemented destructor (which is implemented in a context
where the type being managed is complete)?

See my table above. Anytime you instantiate a member function
of boost::scoped_ptr, the class must be complete. You may,
however, instantiate the class template itself on an incomplete
type. The rules as to when and where a template is implicitly
instantiated are fairly complex, but roughly speaking: a class
template will be instantiated any time it is used in a context
where the compiler requires a complete type, and a member
function or static variable of the class template will be
instantiated whenever they are "used".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top