is this legal?
// Start Header File TEST.h
#include <vector>
class CTest
{
public:
CTest();
~CTest();
private:
struct Incomplete;
std::vector<Incomplete> Vec_;
private:
CTest(const CTest &);
CTest &operator = (const CTest &);
};
// End Header File TEST.h
I wasnt sure of the position of incomplete types with standard containers.
VS2005 does not seem to mind but that is not a benchmark.
Thanks
Stephen Howe
Don't have a quote from the Standard ready, but somehow I recall that
it's not legal. Look through the archives for "instantiate templates
incomplete types C++" (or something like that, without quotes).
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
>Stephen Howe wrote:
>> is this legal?
>
>Don't have a quote from the Standard ready, but somehow I recall that
>it's not legal. Look through the archives for "instantiate templates
>incomplete types C++" (or something like that, without quotes).
Thanks Victor. It is not legal
I found Matt Austerns article here
The Standard Librarian: Containers of Incomplete Types
http://www.drdobbs.com/cpp/184403814
which is useful and good except I dont beleive he is right on one point.
You can have function declarations (not definitions) where the arguments and return type are incomplete types.
You are not restricted to just references or pointers to incomplete types, but the actual types themselves.
Of course you need the full type if, say, defining an inline function.
S
Yes.
To be precise, Matt Austern, then chair of the library working group of the C++
standardization committee, wrote "You can't pass or return incomplete types by
value, for the same reason that you can't define variables of an incomplete
type.", in the context of function declarations, and that's wrong.
Another case of a well-known expert getting this wrong is the GOTW on PIMPL,
where Herb Sutter, chair of the standardization committee, used std::auto_ptr
with incomplete type...
From which we might conclude that committee chairs don't go well with
incomplete types.
Or, there's something there.
Cheers,
- Alf
Greets
That's not the issue, at least not formally. The standard
clearly says that it is undefined behavior to instantiate any of
the templates defined in the standard over an undefined type.
Typically, auto_ptr will only need to know the complete type in
the destructor, and you will get away with it if the destructor
of the pimpl is not inlined. But that's just a case of
undefined behavior happening to work, and you won't get away
with it if the implementation uses concepts. (And the next
version of the standard will, I think, require a diagnostic. At
least if concepts make it in.)
--
James Kanze
Could you explain this to me then? Why was it ever allowed to delete a
pointer to an incomplete type? As I understand it, the compiler must
already know if the type is complete or incomplete. It has to know
which destructor to call at compile time. If it can always detect it,
and it's always an error (aka undefined behavior), why not require a
diagnostic instead of call it undefined behavior? Dittos for deleting
a void pointer. Why allow that? I've hit numerous bugs and wasted a
lot of time tracking down memory leaks because of this IMO silly
mistake in the standard, and because of lack of QoI in modern
commercial compilers. At least gcc will issue warnings about these two
cases with the proper flags, so the script calling gcc can check the
log for these warnings and fail the build. Visual studios
unfortunately will not even issue a warning in one of these cases even
with all of the relevant flags enabled.
I have no idea what are concepts, but does it mean that the compilation
of the code using pimpl is going to fail with the c++0x?
boost::shared_ptr doesn't require for the type to be complete. Since
the new standard will include a similar smart pointer, will it lose that
feature?
--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---
"Juha Nieminen" <nos...@thanks.invalid> wrote in message
news:hkcb1e$hlc$1...@adenine.netfront.net...
Yeah C++0x shared_ptr and unique_ptr do not require type to be complete. I
asked a question elsewhere about the status of auto_ptr. It seems to be
quite common to use auto_ptr to implement the pimpl idiom.
/Leigh
No, I can't explain it. It seems as incongruous to me as it
does to you. IMHO, it should be an error, requiring a
diagnostic.
--
James Kanze
Not if you do it right. (And concepts are simply a means of
getting improved compile time error checking from templates.
The standard says, for example, that a type used to instantiate
std::vector must be assignable and copiable: without concepts,
you don't get an error until you try to instantiate one of the
functions which actually assigns or copies; with concepts, you
can get an error any time you try to instantiate the vector.)
--
James Kanze
I don't think so. IIRC, the wording in the latest draft I saw
said that this case was undefined, unless otherwise specified.
And the description of shared_ptr specified otherwise. (But I
think it's about the only template which did.)
--
James Kanze
[...]
> I
> asked a question elsewhere about the status of auto_ptr. It seems to be
> quite common to use auto_ptr to implement the pimpl idiom.
Does it? My thoughts here are: why bother. You still need an
out of line destructor, and if you have to write a destructor
anyway, what's the problem with putting a delete in it, and
using a raw pointer. The whole point of the compilation
firewall idiom is to reduce include file dependencies, and any
smart pointer will require at least one extra include file.
--
James Kanze
'delete p' where *p is of statically incomplete type T is Undefined Behavior if
T has a non-trivial destructor or deallocation function, by �5.3.5/5.
'delete p' where *p is of static type 'void' is Undefined Behavior by �5.3.5/3,
since there are no actual objects of type 'void'.
There's even a note explaning that particular consequence.
Cheers & hth.,
- Alf
That's the rule. I'm asking why was that ever chosen to be the rule.
Currently, for delete *p, if *p is a complete non-void type with a
trivial destructor and trivial deallocation function, then it's
defined behavior. Otherwise it's undefined behavior. I argue that all
cases of delete *p, where *p is void type or an incomplete type,
should be "diagnostic required". I don't see any good reason for
anything different. The current rules in the standard border on brain
damaged.
You need both an out of line constructor and destructor. Using auto_ptr for
pimpl does in fact reduce file dependencies which is exactly why I do it.
The translation unit containing the ctor and dtor of the class containing
the pimpl will have access to the complete type.
Read Herb Sutter's
http://www.gotw.ca/publications/using_auto_ptr_effectively.htm it advocates
using auto_ptr for pimpl. I use auto_ptr for pimpl but I will change it to
use unique_ptr when I get a C++0x compiler. Yes I am aware that it is
"officially" undefined behaviour to use auto_ptr for pimpl. The only reason
it remains so in C++0x is that auto_ptr has been deprecated as far as I can
tell.
/Leigh
> > Does it? My thoughts here are: why bother. You still need
> > an out of line destructor, and if you have to write a
> > destructor anyway, what's the problem with putting a delete
> > in it, and using a raw pointer. The whole point of the
> > compilation firewall idiom is to reduce include file
> > dependencies, and any smart pointer will require at least
> > one extra include file.
> You need both an out of line constructor and destructor.
Yes, but an inline constructor for the compilation firewall
idiom doesn't make any sense. It's only the destructor which
raises a question.
> Using auto_ptr for pimpl does in fact reduce file dependencies
> which is exactly why I do it.
Using the compilation firewall idiom seriously reduces file
dependencies. Using std::auto_ptr to implement adds one
dependency, on <memory>. And doesn't buy you anything anywhere
else. So why bother. This is a case where a raw pointer is the
simplest and most effective answer.
> The translation unit containing the ctor and dtor of the class
> containing the pimpl will have access to the complete type.
Obviously. That's what's required.
> Read Herb
> Sutter'shttp://www.gotw.ca/publications/using_auto_ptr_effectively.htmit
> advocates using auto_ptr for pimpl.
I've doubtlessly read it, but I'm not convinced. I repeat: what
does std::auto_ptr buy you, compared to a raw pointer?
> I use auto_ptr for pimpl but I will change it to use
> unique_ptr when I get a C++0x compiler. Yes I am aware that
> it is "officially" undefined behaviour to use auto_ptr for
> pimpl. The only reason it remains so in C++0x is that
> auto_ptr has been deprecated as far as I can tell.
I use raw pointers for the compilation firewall idiom. Works
just as well as auto_ptr, and is a lot simpler.
--
James Kanze
std::auto_ptr buys you ownership semantics with RAII (no memory leak if
exception is thrown during construction of class owning the pimpl object).
The alternative is to write your own version of something similar to
std::auto_ptr but why bother? I will use unique_ptr instead when it is
available.
It is silly saying a <memory> dependency is undesirable.
/Leigh
I think that the point is that using std::auto_ptr in the pimpl idiom
doesn't save much work. You still need to write an explicit destructor
(and possibly constructor) and forbid copying and assignment of the
class. Basically you are more or less simply changing a "delete data;"
in the destructor to a "std::auto_ptr" in the class declaration. Not
much of a saving.
"Juha Nieminen" <nos...@thanks.invalid> wrote in message
news:hki1dc$1jhf$1...@adenine.netfront.net...
It is still a saving and saves you from using local (automatic) auto_ptrs in
the constructor body especially if the class has more than one pimpl object
(think about exception safety - a bad_alloc exception could be thrown if you
create more than one pimpl object or anything else that could throw an
exception in the constructor body). "delete data;" in the destructor would
never be called if the constructor body throws an exception.
RAII is one of the features that makes C++ great, not using it is plain
silly.
/Leigh
My 2 cents: Generally when I use pimpl, it's
//header
#include <string>
class Foo
{
public:
Foo();
~Foo();
std::string getName() const; //ex func
//etc.
private:
class FooImpl;
FooImpl * impl;
};
//cpp
class Foo::FooImpl
{
public:
//impl
};
Foo::Foo() : impl(new Foo::FooImpl) {}
Foo::~Foo() { delete impl; }
std::string Foo::getName() const { /*return impl->getName();*/ return
""; }
//end example
Most uses of pimpl are as simple as this. I would be skeptical of a
design which had "multiple pimpl pointers". The constructor body of
Foo is generally that, no more, no less, give or take the names.
However, I suppose there's nothing to lose by using std::auto_ptr
either.
Finally, I feel stupid. Could someone explain to me how this works,
how std::auto_ptr::~auto_ptr "finds" the name Foo as a complete type?
Probably something to do with template lookup rules, the two phase
lookup, lookup based on instantiation context and declaring context,
and the rest of that nastiness. Playing around with it, I proved to
myself that it does work if Foo::~Foo is defined in a scope where
FooImpl is a complete type, and that it deletes it as an incomplete
type if Foo::~Foo is declared in a different translation unit.
I would think that it would be undefined behavior if Foo::~Foo is
defined in a scope where FooImpl is an incomplete type. Interestingly
enough, if I move the Foo::~Foo definition to before the FooImpl class
definition in the same translation unit, a scope where FooImpl is an
incomplete type, visual studios 2008 still deletes it as a complete
type, which completely mystifies me. Is this visual studios protecting
me from myself, and making it "do the right thing" in a case of
undefined behavior? Or do I completely fail at understanding the
template instantiation and name lookup rules?
I suppose the danger with std::auto_ptr is no greater than the danger
with a naked pointer: you get a delete of an incomplete type pointer
either way, and a good compiler will warn you either way. However,
this just bugs me as questionable style as at the point of declaration
of "std::auto_ptr<Foo::FooImpl> Foo::impl", the type FooImpl is an
incomplete type. That just seems like a bad idea, or something.
Perhaps if I better understood the template magic I would not think
so.
Not really, there is nothing wrong with a top-level "model" class containing
multiple objects that you don't want to expose in the model header file to
reduce dependencies and compilation times.
>
> I would think that it would be undefined behavior if Foo::~Foo is
> defined in a scope where FooImpl is an incomplete type. Interestingly
> enough, if I move the Foo::~Foo definition to before the FooImpl class
> definition in the same translation unit, a scope where FooImpl is an
> incomplete type, visual studios 2008 still deletes it as a complete
> type, which completely mystifies me. Is this visual studios protecting
> me from myself, and making it "do the right thing" in a case of
> undefined behavior? Or do I completely fail at understanding the
> template instantiation and name lookup rules?
>
Comeau complains if you do this so I wouldn't trust an individual compiler's
behaviour as to what constitutes undefined behaviour or not.
/Leigh
I believe there is a point to this and that is exception safety.
Suppose you have 2 + pointers that you new in the constructor
Suppose further that one of them might throw (or further down the line, another member object)
The constructor wiill gurantee that already constructed member objects will have the their destructors called before the throw
continues. But the bald pointers are not objects therefore there is a memory leak. delete or delete[] is not called.
In contrast, auto_ptr's will be correctly destroyed.
The lesson is if you think your sub-objects might throw
=> better use a RAII technique to wrap those bald pointers.
Any container is better than none.
This is about the only benefits of using auto_ptr here.
Stephen Howe
> std::auto_ptr buys you ownership semantics with RAII (no
> memory leak if exception is thrown during construction of
> class owning the pimpl object). The alternative is to write
> your own version of something similar to std::auto_ptr but why
> bother? I will use unique_ptr instead when it is available.
Sorry, but that's just false. Whether you use auto_ptr or a raw
pointer changes absolutely nothing during construction.
> It is silly saying a <memory> dependency is undesirable.
It's not really a big point, but why add the dependency if it
doesn't buy you anything.
--
James Kanze
If your main object contains more than one pointer, it's not the
compilation firewall idiom---you're doing something else, and
other rules apply. And as long as there's only one pointer,
auto_ptr buys you absolutely nothing.
> "delete data;" in the destructor would never be called if the
> constructor body throws an exception.
There is no "constructor body" in the compilation firewall
idiom. The constructor, by definition, is simply:
MyType::MyType(...) : myImpl( new Impl(...) ) {}
Anything else, and you're not talking about the compilation
firewall idiom, but something else.
> RAII is one of the features that makes C++ great, not using it
> is plain silly.
Like everything else, you use it when appropriate. When it buys
you something.
(And using smart pointers in a class object really isn't the
normal use of RAII, which depends on deterministic destructor
call.)
--
James Kanze
> //header
> #include <string>
> class Foo
> {
> public:
> Foo();
> ~Foo();
> std::string getName() const; //ex func
> //etc.
> private:
> class FooImpl;
> FooImpl * impl;
> };
> //cpp
> class Foo::FooImpl
> {
> public:
> //impl
> };
> Foo::Foo() : impl(new Foo::FooImpl) {}
> Foo::~Foo() { delete impl; }
> std::string Foo::getName() const { /*return impl->getName();*/ return
> "";
> }
> //end example
> Most uses of pimpl are as simple as this.
That's how Herb Sutter defined it, and since he invented the
term, I think it just adds to the confusion if you decide to use
some other definition. (It's not a term like OO, which already
has hundreds of subtly different definitions.)
> I would be skeptical of a design which had "multiple pimpl
> pointers". The constructor body of Foo is generally that, no
> more, no less, give or take the names. However, I suppose
> there's nothing to lose by using std::auto_ptr either.
Except for an additional include in the header, no. And perhaps
a false sense of security; typically, when you use auto_ptr in
an object, you don't need to provide a user defined destructor.
> Finally, I feel stupid. Could someone explain to me how this
> works, how std::auto_ptr::~auto_ptr "finds" the name Foo as a
> complete type? Probably something to do with template lookup
> rules, the two phase lookup, lookup based on instantiation
> context and declaring context, and the rest of that nastiness.
> Playing around with it, I proved to myself that it does work
> if Foo::~Foo is defined in a scope where FooImpl is a complete
> type, and that it deletes it as an incomplete type if
> Foo::~Foo is declared in a different translation unit.
> I would think that it would be undefined behavior if Foo::~Foo
> is defined in a scope where FooImpl is an incomplete type.
In fact, it's undefined behavior if the class is even
instantiated in a context where Foo is not complete. I wouldn't
be surprised if some implementations complained.
> Interestingly enough, if I move the Foo::~Foo definition to
> before the FooImpl class definition in the same translation
> unit, a scope where FooImpl is an incomplete type, visual
> studios 2008 still deletes it as a complete type, which
> completely mystifies me. Is this visual studios protecting me
> from myself, and making it "do the right thing" in a case of
> undefined behavior? Or do I completely fail at understanding
> the template instantiation and name lookup rules?
It's undefined behavior; in practice, it will depend on how the
compiler instantiates templates.
--
James Kanze
[...]
> > Most uses of pimpl are as simple as this. I would be
> > skeptical of a design which had "multiple pimpl pointers".
> > The constructor body of Foo is generally that, no more, no
> > less, give or take the names. However, I suppose there's
> > nothing to lose by using std::auto_ptr either.
> Not really, there is nothing wrong with a top-level "model"
> class containing multiple objects that you don't want to
> expose in the model header file to reduce dependencies and
> compilation times.
There's nothing wrong with a top-level model class doing a lot
of things; most of the ones I deal with today contain things
like std::vector<double>, for example. But we were talking here
about the pimpl idiom. As defined by Herb Sutter. (Formally,
it's really the compilation firewall idiom, and before Herb
popularized it, it was often known as the Cheshire cat
idiom---named by Jim Adcock?) It's a very specific idiom.
> > I would think that it would be undefined behavior if
> > Foo::~Foo is defined in a scope where FooImpl is an
> > incomplete type. Interestingly enough, if I move the
> > Foo::~Foo definition to before the FooImpl class definition
> > in the same translation unit, a scope where FooImpl is an
> > incomplete type, visual studios 2008 still deletes it as a
> > complete type, which completely mystifies me. Is this visual
> > studios protecting me from myself, and making it "do the
> > right thing" in a case of undefined behavior? Or do I
> > completely fail at understanding the template instantiation
> > and name lookup rules?
> Comeau complains if you do this so I wouldn't trust an
> individual compiler's behaviour as to what constitutes
> undefined behaviour or not.
The standard says that instantiating std::auto_ptr (or any
template defined in the standard) over an incomplete type is
undefined behavior). In this case, the standard probably could
have been a bit laxer, and only required a complete type where
the destructor was instantiated. But the standard having
specified this restriction, it wouldn't surprise me if some
compiler/library implementation enforced it. (G++ enforces a
lot of this sort of thing in its library implementation; I don't
know off hand if this particular case is covered or not,
however.)
--
James Kanze
I think Leigh is referring to something like
Foo::Foo()
: myImpl( std::auto_ptr<Impl>( new Impl ) )
{
// Statement X that might trow
}
If X throws then statements in ~Foo are not executed.
However, the smart pointer ensures that the Impl instance is deleted.
This avoids doing silly things like
Foo::Foo()
{
std::auto_ptr<Impl> pImpl( new Impl );
myImpl = pImpl.get();
// Statement X that might throw
pImpl.release();
}
or even worse with try-catch.
A main catch is that one is using knowledge of how a std::auto_ptr is usually
implemented in order to create code that will work in practice in spite of
formally Undefined Behavior, which is a bit fragile...
And really, what's used is not std::auto_ptr's ownership transfer semantics, but
just its RAII cleanup.
And so even the simplest of the simplest RAII cleanup class would do as a
well-defined substitute, e.g.
template< typename T >
struct AutoDestroyed
{
T* p;
~AutoDestroyed() { destroy( p ); } // Define 'destroy' per type T.
AutoDestroyed( T* const p_ ): p(p_) {}
AutoDestroyed( AutoDestroyed const& );
AutoDestroyed& operator=( AutoDestroyed const& );
};
>> It is silly saying a <memory> dependency is undesirable.
>
> It's not really a big point, but why add the dependency if it
> doesn't buy you anything.
See above.
Cheers,
- Alf
"James Kanze" <james...@gmail.com> wrote in message
news:4db3fe66-d76b-45f1...@j31g2000yqa.googlegroups.com...
> On Feb 4, 11:07 pm, "Leigh Johnston" <le...@i42.co.uk> wrote:
>> > I've doubtlessly read it, but I'm not convinced. I repeat:
>> > what does std::auto_ptr buy you, compared to a raw pointer?
>
>> std::auto_ptr buys you ownership semantics with RAII (no
>> memory leak if exception is thrown during construction of
>> class owning the pimpl object). The alternative is to write
>> your own version of something similar to std::auto_ptr but why
>> bother? I will use unique_ptr instead when it is available.
>
> Sorry, but that's just false. Whether you use auto_ptr or a raw
> pointer changes absolutely nothing during construction.
>
It is not false, auto_ptr gives you RAII. Assigning to a raw pointer the
result of new in a ctor-initializer list is not using RAII and can leak if
subsequent ctor code throws an exception. Perhaps pimpl is the wrong term
here, it is the method by which you can reduce the number of #includes in
your code:
struct foo;
struct bar;
struct baz;
struct model
{
/* ... */
std::auto_ptr<foo> fooObject;
std::auto_ptr<bar> barObject;
std::auto_ptr<baz> bazObject;
};
We don't want users who #include "model.h" to be forced to include foo.h,
bar.h and baz.h unless they are interested in such objects.
pimpl idiom is a special case of the above using just one object instead of
N objects with a more restrictive header file policy (i.e. pimpl class will
usually only be seen by the TU containing the pimpl parent class).
/Leigh