Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

class definition containing member of own type

3 views
Skip to first unread message

Phil

unread,
Feb 17, 2004, 8:23:29 PM2/17/04
to
Hi,
Sorry for the poor Subject line but I'm unsure of the correct term for
the code I've written (which has limited my ability to search for an
answer on my own). I wrote the following code fully expecting it to
fail...

#include <vector>

using std::vector;

class MyClass
{
vector<MyClass> myContainer;

public:
void Add(MyClass);
};

void MyClass::Add(MyClass NewClass)
{
myContainer.push_back(NewClass);
}

int main()
{
MyClass mc;
MyClass mc2;
mc.Add(mc2);
return 0;
}

...but it did not. I was unable to understand how the compiler would
allow me to declare a vector of MyClass inside the definition of
MyClass itself. My only assumption is that the type of the vector is
not checked until after the class has been created (perhaps at
runtime?). If I try to declare a MyClass directly inside the
definition I recieve variable 'has incomplete type' error.

Before I investigated further I wanted to ensure that I was not
exploiting a 'feature' of my compiler (GCC 3.2.3/MinGW). If I am then
I apologise for posting on this group. If I am not, and this is valid
C++, is this a poor technique?

Many thanks in advance for any help you can offer,
Phil

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Jeff Schwab

unread,
Feb 18, 2004, 7:24:22 AM2/18/04
to

It's fine, as long as the vector starts its life empty. The vector
doesn't actually have any member that's a MyClass, only a pointer to a
dynamically allocated array of MyClass. It's almost as though you'd
done something like this:

struct My_class
{
My_class( ): p( 0 ) { }
~My_class( ) { delete p; }

void add( My_class c ) { p = new My_class( c ); }

My_class* p;
};

int main( )
{
My_class mc;
My_class mc2;

mc.add( mc2 );

Jonathan Turkanis

unread,
Feb 18, 2004, 10:45:24 AM2/18/04
to

"Phil" <zzx...@hotmail.com> wrote in message:

> ...but it did not. I was unable to understand how the compiler would
> allow me to declare a vector of MyClass inside the definition of
> MyClass itself. My only assumption is that the type of the vector is
> not checked until after the class has been created (perhaps at
> runtime?). If I try to declare a MyClass directly inside the
> definition I recieve variable 'has incomplete type' error.

All template instantiation occurs at compile time.

It's possible (and typical) to implement vector so that the only
member with type related to the vector's value_type is a pointer. In
this case, C++ language rules allow a type T to have a non-static
member of type vector<T>. However, the container requirements classify
instantiating a library container with an incomplete type as undefined
behavior.(17.4.3.6/2)

Jonathan

bert hubert

unread,
Feb 18, 2004, 4:08:14 PM2/18/04
to
On 2004-02-18, Phil <zzx...@hotmail.com> wrote:

> class MyClass
> {
> vector<MyClass> myContainer;
> public:
> void Add(MyClass);
> };

I think the important thing to note that at instantiation, the vector only
needs to know about the interface of MyClass, just as when storing pointers.
Furthermore, the amount of stack the vector will need is known as well.

So the compiler knows everything it needs to know in order to make a
MyClass, unlike when MyClass is in the same object as MyClass.

Put it this way:

struct MyClass
{
MyClass a;
};

Now, this won't work as we need to know the size of MyClass to know the size
of MyClass.

struct MyClass
{
MyClass* a;
};

This obviously works. Now, a vector deals internally with objects on the
heap, so it has a contiguous amount of memory which it references using
MyClass* pointers to give you access to it. So it contains no MyClass per
se, but a MyClass*.

Bert

--
http://www.PowerDNS.com/ Open Source Database Driven Nameserver
http://lartc.org Linux Advanced Routing & Traffic Control HOWTO

Christoph Rabel

unread,
Feb 18, 2004, 6:06:26 PM2/18/04
to
Phil wrote:
> using std::vector;
>
> class MyClass
> {
> vector<MyClass> myContainer;

Here you have undefined behaviour. It is not allowed to use
an incomplete type with a Container of the standard library.

This is stated in Chapter 17 of the Standard, which
discusses the standard library and its components.

17.4.3.6 Other functions paragraph 2

In particular, the effects are undefined in the following cases:

— if an incomplete type (3.9) is used as a template argument
when instantiating a template component.

> ...but it did not. I was unable to understand how the compiler would
> allow me to declare a vector of MyClass inside the definition of
> MyClass itself. My only assumption is that the type of the vector is
> not checked until after the class has been created (perhaps at
> runtime?). If I try to declare a MyClass directly inside the
> definition I recieve variable 'has incomplete type' error.

Probably in your implementation of vector MyClass is only
used as a pointer or reference in the code.

> Before I investigated further I wanted to ensure that I was not
> exploiting a 'feature' of my compiler (GCC 3.2.3/MinGW). If I am then
> I apologise for posting on this group. If I am not, and this is valid
> C++, is this a poor technique?

It is undefined behaviour.

hth

Christoph

Squider

unread,
Feb 18, 2004, 6:12:18 PM2/18/04
to
Phil wrote:

Note that the vector is initially empty, therefore it's completely legal.

And yeah, you can't create vectors of incomplete types, pointers and
references are OK, though.

--
Jukka Helle (Squider) <squidysof...@dnainternet.net>
<http://squidysoft.tk/>

John Potter

unread,
Feb 19, 2004, 5:21:05 AM2/19/04
to
On 17 Feb 2004 20:23:29 -0500, zzx...@hotmail.com (Phil) wrote:

> Sorry for the poor Subject line but I'm unsure of the correct term for
> the code I've written (which has limited my ability to search for an
> answer on my own). I wrote the following code fully expecting it to
> fail...

> class MyClass


> {
> vector<MyClass> myContainer;
> public:
> void Add(MyClass);
> };

> ...but it did not. I was unable to understand how the compiler would


> allow me to declare a vector of MyClass inside the definition of
> MyClass itself.

There is a CUJ article my Matt Austern where he also expresses surprise
that vector<self> compiled. He goes on to explain why it might and why
it could not possibly work for map. It does work for map with gcc.

> My only assumption is that the type of the vector is
> not checked until after the class has been created (perhaps at
> runtime?).

No, it is just a matter of indirection.

> If I try to declare a MyClass directly inside the
> definition I recieve variable 'has incomplete type' error.

This would be a recursive definition, but adding a MyClass* is not and
that is what vector likely contains.

> Before I investigated further I wanted to ensure that I was not
> exploiting a 'feature' of my compiler (GCC 3.2.3/MinGW).

You are. The effects of using an incomplete type to instantiate a
standard library component are undefined. Working fine is one
possibility, but it is not portable.

> If I am then
> I apologise for posting on this group. If I am not, and this is valid
> C++, is this a poor technique?

It is a very nice technique that many would like to be well defined.

class Tree {
private :
vector<Tree> children;

John

James Dennett

unread,
Feb 19, 2004, 5:31:13 AM2/19/04
to

I think that's not portably true; the C++ standard, if I
recall correctly, would say that using std::vector<T> for
an incomplete type T gives undefined behaviour.

> The vector
> doesn't actually have any member that's a MyClass, only a pointer to a
> dynamically allocated array of MyClass.

But it's certainly allowed to have a member such as

char array[sizeof(MyClass)];

> It's almost as though you'd
> done something like this:
>
> struct My_class
> {
> My_class( ): p( 0 ) { }
> ~My_class( ) { delete p; }
>
> void add( My_class c ) { p = new My_class( c ); }
>
> My_class* p;
> };
>
> int main( )
> {
> My_class mc;
> My_class mc2;
>
> mc.add( mc2 );
> }

For some (or most, or all) existing implementations, you may
well be able to "get away with" using vector<incomplete>.

-- James.

Thomas Mang

unread,
Feb 19, 2004, 9:05:47 AM2/19/04
to

Jonathan Turkanis schrieb:

> "Phil" <zzx...@hotmail.com> wrote in message:
>
> > ...but it did not. I was unable to understand how the compiler would
> > allow me to declare a vector of MyClass inside the definition of
> > MyClass itself. My only assumption is that the type of the vector is
> > not checked until after the class has been created (perhaps at
> > runtime?). If I try to declare a MyClass directly inside the
> > definition I recieve variable 'has incomplete type' error.
>
>

[snip]

> However, the container requirements classify
> instantiating a library container with an incomplete type as undefined
> behavior.(17.4.3.6/2)

Where is the point-of-instantiation for std::vector<MyClass>? Is it right
were the data member is declared, or before the definition of class
MyClass, or at the end of MyClass - definition?


regards,

Thomas

Jeff Schwab

unread,
Feb 20, 2004, 4:05:55 PM2/20/04
to

NB: I've snipped liberally.

James Dennett wrote:
> Jeff Schwab wrote:
> > Phil wrote:
> > > I wrote the following code fully expecting it to fail...
> > >
> > > #include <vector>
> > >
> > > using std::vector;
> > >
> > > class MyClass
> > > {
> > > vector<MyClass> myContainer;

> > > };
> > >
> > >
> > > ...but it did not.

> > It's fine, as long as the vector starts its life empty.
>
> I think that's not portably true; the C++ standard, if I
> recall correctly, would say that using std::vector<T> for
> an incomplete type T gives undefined behaviour.

That's not what the standard says, even if that's what was meant.
17.4.3.6 lists restrictions on the "components" supplied by a C++
program for use by the Standard Library. That section says it's UB to
use an incomplete type as a template argument when instantiating a
template component. However, in this case, no component is being
instantiated.

> > The vector
> > doesn't actually have any member that's a MyClass, only a pointer to a
> > dynamically allocated array of MyClass.
>
> But it's certainly allowed to have a member such as
>
> char array[sizeof(MyClass)];

Is it?

> For some (or most, or all) existing implementations, you may
> well be able to "get away with" using vector<incomplete>.

I could understand why an implementer might not want to support
vector<incomplete>, but I don't see where the standard explicitly
prohibits it.

Ben Hutchings

unread,
Feb 20, 2004, 4:10:26 PM2/20/04
to
Thomas Mang wrote:
> Jonathan Turkanis schrieb:
>
>> "Phil" <zzx...@hotmail.com> wrote in message:
>>
>> > ...but it did not. I was unable to understand how the compiler would
>> > allow me to declare a vector of MyClass inside the definition of
>> > MyClass itself.
<snip>
>> However, the container requirements classify
>> instantiating a library container with an incomplete type as undefined
>> behavior.(17.4.3.6/2)
>
> Where is the point-of-instantiation for std::vector<MyClass>? Is it right
> were the data member is declared, or before the definition of class
> MyClass, or at the end of MyClass - definition?

It is before the definition of class MyClass, because "Non-static
(9.4) members that are class objects shall be objects of previously
defined classes." (9.2/8)

Dylan Nicholson

unread,
Feb 21, 2004, 8:53:13 AM2/21/04
to
John Potter <jpo...@falcon.lhup.edu> wrote in message news:<ome630hlbm01vtr37...@news.east.earthlink.net>...

> On 17 Feb 2004 20:23:29 -0500, zzx...@hotmail.com (Phil) wrote:
>
> > Sorry for the poor Subject line but I'm unsure of the correct term for
> > the code I've written (which has limited my ability to search for an
> > answer on my own). I wrote the following code fully expecting it to
> > fail...
>
> > class MyClass
> > {
> > vector<MyClass> myContainer;
> > public:
> > void Add(MyClass);
> > };
>
> > ...but it did not. I was unable to understand how the compiler would
> > allow me to declare a vector of MyClass inside the definition of
> > MyClass itself.
>
> There is a CUJ article my Matt Austern where he also expresses surprise
> that vector<self> compiled. He goes on to explain why it might and why
> it could not possibly work for map. It does work for map with gcc.
>
FWIW, the only container for which incomplete types seem to work
across a wide range of compilers is std::deque<>. Not sure why this
should be, or whether my definition of 'wide' is at all useful (at
last count it was about 6 different compilers over 6 platforms), but
I've been tempted to exploit it more than once (but found a better way
in the end).

Dylan

James Dennett

unread,
Feb 21, 2004, 9:01:37 AM2/21/04
to
Jeff Schwab wrote:
> NB: I've snipped liberally.
>
> James Dennett wrote:
>
>>Jeff Schwab wrote:
>> > Phil wrote:
>> > > I wrote the following code fully expecting it to fail...
>> > >
>> > > #include <vector>
>> > >
>> > > using std::vector;
>> > >
>> > > class MyClass
>> > > {
>> > > vector<MyClass> myContainer;
>> > > };
>> > >
>> > >
>> > > ...but it did not.
>
>
>> > It's fine, as long as the vector starts its life empty.
>>
>>I think that's not portably true; the C++ standard, if I
>>recall correctly, would say that using std::vector<T> for
>>an incomplete type T gives undefined behaviour.
>
>
> That's not what the standard says, even if that's what was meant.
> 17.4.3.6 lists restrictions on the "components" supplied by a C++
> program for use by the Standard Library. That section says it's UB to
> use an incomplete type as a template argument when instantiating a
> template component. However, in this case, no component is being
> instantiated.

It looks very much as if there's an instantiation of
std::vector there, so long as we distinguish between
instantiation of a class template to generate a class
and instantiation of a type to generate an object.

Here you're instantiating std::vector, and must not
supply an incomplete type to do so.

Is there some reason for your claim that no component
is being instantiated? Do you not agree that the
"component" std::vector is instantiated in this case?

>> > The vector
>> > doesn't actually have any member that's a MyClass, only a pointer to a
>> > dynamically allocated array of MyClass.
>>
>>But it's certainly allowed to have a member such as
>>
>> char array[sizeof(MyClass)];
>
>
> Is it?

I believe so, precisely because MyClass is required to be
complete.

>>For some (or most, or all) existing implementations, you may
>>well be able to "get away with" using vector<incomplete>.
>
>
> I could understand why an implementer might not want to support
> vector<incomplete>, but I don't see where the standard explicitly
> prohibits it.

17.4.3.6 seems to cover it.

Jeff Schwab

unread,
Feb 21, 2004, 8:48:54 PM2/21/04
to

The std::vector is not the component. MyClass is the component. See
the Standard.

>>>>The vector
>>>>doesn't actually have any member that's a MyClass, only a pointer to a
>>>>dynamically allocated array of MyClass.
>>>
>>>But it's certainly allowed to have a member such as
>>>
>>> char array[sizeof(MyClass)];
>>
>>
>>Is it?
>
> I believe so, precisely because MyClass is required to be
> complete.

Even if MyClass is complete, it does not imply that the sizeof the
component type is available at the time of the container's
instantiation. Of course, I would not be shocked to learn that char
array[sizeof(MyClass)] were allowed. :)

>>>For some (or most, or all) existing implementations, you may
>>>well be able to "get away with" using vector<incomplete>.
>>
>>
>>I could understand why an implementer might not want to support
>>vector<incomplete>, but I don't see where the standard explicitly
>>prohibits it.
>
>
> 17.4.3.6 seems to cover it.

Where?

Jeff Schwab

unread,
Feb 22, 2004, 5:56:42 AM2/22/04
to
>>>>>>#include <vector>
>>>>>>
>>>>>>using std::vector;
>>>>>>
>>>>>>class MyClass
>>>>>>{
>>>>>> vector<MyClass> myContainer;
>>>>>>};

<snip/>

> Even if MyClass is

read: happens, in a particular program, to be

> complete, it does not imply that the sizeof the
> component type is

always guaranteed to be

> available at the time of the container's
> instantiation. Of course, I would not be shocked to learn that char
> array[sizeof(MyClass)] were allowed. :)

0 new messages