Forward declaration of classes

A

Andrea Crotti

In one class I need a member which is a vector of objects of the same
class.
The only way I found is to declare first the class empty and then
declare it again

--8<---------------cut here---------------start------------->8---
class X;

class X {
....

};
--8<---------------cut here---------------end--------------->8---

but it's not so beautiful, any other way?
 
V

Victor Bazarov

In one class I need a member which is a vector of objects of the same
class.
The only way I found is to declare first the class empty and then
declare it again

You're confusing the declaration and definition. A definition (the part
that you have later in the code) is a declaration as well, but in this
particular case multiple declarations don't hurt, since they are all the
same.

What you did is pretty much what you can do, first declare and then define.

Now, there is another problem. You aren't supposed to use an incomplete
class as the template argument. Some compilers let you slide on that,
and some don't like it.
--8<---------------cut here---------------start------------->8---
class X;

class X {
...

};
--8<---------------cut here---------------end--------------->8---

but it's not so beautiful, any other way?

Not really.

V
 
A

Andrea Crotti

Victor Bazarov said:
You're confusing the declaration and definition. A definition (the
part that you have later in the code) is a declaration as well, but in
this particular case multiple declarations don't hurt, since they are
all the same.

What you did is pretty much what you can do, first declare and then define.

Now, there is another problem. You aren't supposed to use an
incomplete class as the template argument. Some compilers let you
slide on that, and some don't like it.

Ah I see the problem, well g++ didn't give any warning and we don't use
other compilers so I guess it could be fine.

What my predecessor did was having a Node which has a list of
Neighbours, and the Neighbour is actually really the Node class.

A node than was also neighbour of itself, so well it was quite horrific
from a OO point of view...
 
B

Balog Pal

Victor Bazarov said:
Now, there is another problem. You aren't supposed to use an incomplete
class as the template argument.

Why? AFAIK it must work the very same way as for anything else. If the
instantiation of the template has code that requires a complete typem, then
it must be diagnosed. If incomplete type is okay in the context, it is well
formed.

However the standard library states that it is undefined behavior to
instantiate any of its classes with an incompletet type, for the curent
version, and for C++0x there is a whitelist of classes that are specified to
be exempt from the restriction. Like shared_ptr.
 
Ö

Öö Tiib

Why?  AFAIK it must work the very same way as for anything else. If the
instantiation of the template has code that requires a complete typem, then
it must be diagnosed. If incomplete type is okay in the context, it is well
formed.

Victor was probably about the third option when usage of incomplete
type may be well-formed and may be undefined behavior depending on
that incomplete type and that some templates (most notably standard
library) do not sort out between such cases.
However the standard library states that it is undefined behavior to
instantiate any of its classes with an incompletet type, for the curent
version, and for C++0x there is a whitelist of classes that are specified to
be exempt from the restriction. Like shared_ptr.

Interesting, how the implementors implement such guarantees into
standard library? Diagnostics that "some undocumented typedef
blah_blah_related_to_issue_nature within library does not compile for
cryptic reason" are great for an enthusiast library like boost. From a
standard implementation i would expect slightly more foolproof
diagnostics.
 
A

Andrea Crotti

Another similar problem:

I have a class containing a vector whose size will be known at run time
but will probably never change.
Then I want to have another class with a vector of this vectors, but I
should pass also the size to the declaration, because I don't have a
0-argument constructor.

Should I just create one and then push the values maybe?

std::vector<PadCoordinate> address;
 
J

James Kanze

On 10/27/2010 10:25 AM, Andrea Crotti wrote:
You're confusing the declaration and definition. A definition
(the part that you have later in the code) is a declaration as
well, but in this particular case multiple declarations don't
hurt, since they are all the same.
What you did is pretty much what you can do, first declare and
then define.
Now, there is another problem. You aren't supposed to use an
incomplete class as the template argument. Some compilers let
you slide on that, and some don't like it.

Probably just a question of wording, but as stated, what you've
said is false. There's absolutely no restriction in the
language with regards to instantiating a template over an
incomplete type, as long as there is nothing in the
instantiation which requires the type to be complete. Whether
this is guaranteed to be the case or not is up to the author of
the template.

What you doubtlessly meant is that it is undefined behavior to
instantiate a template in the standard library using an
incomplete type, because the standard library imposes this
requirement. (In other words, the standard library allows the
implementor to assume that the type is complete everywhere in
the template.) Typically, you can get away with it sometimes,
but not always; some of the better implementations add special
code (static constraints checking) to ensure that you will get
a compiler error instead of undefined behavior (but in practice,
in all but a very few cases, the "undefined behavior" will be
that the code will either work, or it won't compile).

With regards to the OP's problem, the only real solution I see
is a vector of pointers, a pointer to an incomplete type is
a complete type, e.g.:

class Toto; // incomplete type.
Toto* p; // p has a complete type.
 
J

James Kanze

Another similar problem:
I have a class containing a vector whose size will be known at run time
but will probably never change.
Then I want to have another class with a vector of this vectors, but I
should pass also the size to the declaration, because I don't have a
0-argument constructor.
Should I just create one and then push the values maybe?
std::vector<PadCoordinate> address;

It's not too clear what you're trying to do. Something like
this, perhaps:

class Toto
{
std::vector<std::vector<Titi> > member;
public:
Toto(int n, int m) : member(n, std::vector<Titi>(m)) {}
};
 
A

Andrea Crotti

James Kanze said:
It's not too clear what you're trying to do. Something like
this, perhaps:

class Toto
{
std::vector<std::vector<Titi> > member;
public:
Toto(int n, int m) : member(n, std::vector<Titi>(m)) {}
};

In the end it would be more or less the same, but I need more
abstraction and composition, a vector of vector is too "fixed".

That problem now is fixed anyway in a cleaner way...

Now instead suppose I want a map like

--8<---------------cut here---------------start------------->8---
map<PadNodeID, RingBuffer<PadNodeID>>
--8<---------------cut here---------------end--------------->8---

where RingBuffer wants the maximal size passed in and PadNodeID()
exist...
What should I have in the allocator or the constructor here to make this work?
 
T

Trigve

What you doubtlessly meant is that it is undefined behavior to
instantiate a template in the standard library using an
incomplete type, because the standard library imposes this
requirement...

I think c++0x standard (n3126) says in 17.6.3.8/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,
unless specifically allowed for that component."

If component says that template could be incomplete type it is not UB.
See for example 20.9.10 unique_ptr:

"... The template parameter T of unique_ptr may be an incomplete
type."

Trigve
 
B

Balog Pal

Trigve said:
I think c++0x standard (n3126) says in 17.6.3.8/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,
unless specifically allowed for that component."

This is new in C++0x. The current standard does not have components with
that allowance. One of the main featires of shared_ptr was to play well with
incomplete types, so when it entered the standard, opened the
incomplete-type-tolerant line. ;) The baseline is still UB.

Certainly we know that ttuff said UB by the standard can be defined -- in
any way -- by the implementation, and other following layers.
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top