when are declarations sufficent and when do we need definitions?

J

Jess

Hello,

Sometimes declarations are all what we need when we define/declare
classes (or functions?), but sometimes we need definitions. I learned
that if we define a class (B) that has an object (a_obj) of a class
type (A), then we need to define A as well, but if B has a pointer to
A, then we only need to forward declare A. I was told this is because
the compiler needs to see the implemenation of A when allocating
memory for a_obj. However, I think compilers won't allocate any
memory until we create an object of class B. Therefore, if I have
a .cpp file

class A;

class B{
A a_obj;
};

then it seems ok to forward declare A, because there's no object of
class B created. So in this case, why do I still have to define A (or
include it's header file)?

Similarly, we can forward declare A if we only declare a function like

A f(A a);

but we need A's implemenation when we define "f"

A f(A a){...}

If my .cpp file only has f's definition without any program that calls
"f", then shouldn't it be perfectly safe to forward declare A?

I guess my general question is when can we use declarations without
definitions?

Another question about forward declaration is that why do we use

class A;

instead of

extern class A;

I think the latter says A is a class defined somewhere else.

Thanks a lot,
Jess
 
Z

Zeppe

Jess said:
Hello,

Sometimes declarations are all what we need when we define/declare
classes (or functions?), but sometimes we need definitions. I learned
that if we define a class (B) that has an object (a_obj) of a class
type (A), then we need to define A as well, but if B has a pointer to
A, then we only need to forward declare A. I was told this is because
the compiler needs to see the implemenation of A when allocating
memory for a_obj.

No, it is not related to allocation. It's related to the semantics. The
rule of thumb is: "when the program doesn't need to know anything about
the class, the forward declaration suffices". That is, if you merely
have a pointer (which is an address of memory) to a class, there is no
need to know anything about the class itself. On the other side, if you
want to use methods of the class, or if you have a member that is not a
pointer, additional information related to the class (such the class
dimension or its members) are required, and the definition is needed.

Regards,

Zeppe
 
V

Victor Bazarov

Jess said:
Sometimes declarations are all what we need when we define/declare
classes (or functions?), but sometimes we need definitions. I learned
that if we define a class (B) that has an object (a_obj) of a class
type (A), then we need to define A as well, but if B has a pointer to

...or a reference to..
A, then we only need to forward declare A. I was told this is because
the compiler needs to see the implemenation of A when allocating
memory for a_obj. However, I think compilers won't allocate any
memory until we create an object of class B. Therefore, if I have
a .cpp file

class A;

class B{
A a_obj;
};

then it seems ok to forward declare A, because there's no object of
class B created. So in this case, why do I still have to define A (or
include it's header file)?

The compiler needs to calculate the size of a 'B' object. For that it
needs to know what the size of an 'A' object is. It cannot know the
size of an 'A' object without knowing what it consists of.
Similarly, we can forward declare A if we only declare a function like

A f(A a);

but we need A's implemenation when we define "f"

A f(A a){...}

If my .cpp file only has f's definition without any program that calls
"f", then shouldn't it be perfectly safe to forward declare A?

The standard does not prohibit from incomplete types used in function
declarations, but in definitions you cannot use incomplete types. There
is no special provisions for the functions that aren't called within
your own code. The code for them has to be generated, so the parameters
have to have complete types.
I guess my general question is when can we use declarations without
definitions?

What do you mean by "use"?
Another question about forward declaration is that why do we use

class A;

instead of

extern class A;

I think the latter says A is a class defined somewhere else.

"extern" is not allowed with a class declaration. Only with objects
and functions.

V
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Hello,

Sometimes declarations are all what we need when we define/declare
classes (or functions?), but sometimes we need definitions. I learned
that if we define a class (B) that has an object (a_obj) of a class
type (A), then we need to define A as well, but if B has a pointer to
A, then we only need to forward declare A. I was told this is because
the compiler needs to see the implemenation of A when allocating
memory for a_obj. However, I think compilers won't allocate any
memory until we create an object of class B. Therefore, if I have
a .cpp file

class A;

class B{
A a_obj;
};

then it seems ok to forward declare A, because there's no object of
class B created. So in this case, why do I still have to define A (or
include it's header file)?

It's true that the compiler will not allocate memory for an object of
type B until you create such an object. But the compiler will need to
know how A looks like when you define B since the definition of B should
contain all the compiler needs to know to be able to create an object of
type B. But if the compiler does not know what A is in the definition of
B, how can it know what B is?
Similarly, we can forward declare A if we only declare a function like

A f(A a);

but we need A's implemenation when we define "f"

A f(A a){...}

If my .cpp file only has f's definition without any program that calls
"f", then shouldn't it be perfectly safe to forward declare A?

Once again, the definition should contain all information the compiler
need, in this case it must know the size of A so that it can generate
the code for f(). If you then don't use f() a smart compiler might be
able get rid of f() as an optimization, but the standard does not
require it, so a not-so-smart compiler must be able to compile the code
also.
I guess my general question is when can we use declarations without
definitions?

Usually it's a question of size, for many things (like a member of a
class) the compiler needs to know the size. As long as you use pointers
of references the compiler does not need to know more since the sizes of
pointers and references are known. If you in some way try to use a
pointer/reference (like calling a member-function) you'll need the
definition.
Another question about forward declaration is that why do we use

class A;

instead of

extern class A;

extern is used when you tell the compiler about an existing variable
that was not defined in the current compilation unit. So when you write
something like

extern A myA;

you tell the compiler that there exists a variable of type A called myA.
In the case of forward declarations you use the word "class" (notice
that I didn't use "class" in the above extern declaration) to tell the
compiler that you are talking about a type and not a variable. While one
could have used

extern class A;

as forward declarations it's not needed since the compiler does not
particularly care where the type is defined, as long as it is somewhere.
Notice also that this would be a new kind of use of the word extern,
which means that the definition is external to the compilation unit,
since in most cases a forward declaration is followed by the real
declaration later in the file or by some included file (in other words
the forward declaration and definition is in the same compilation unit),
like this:

class B;

class A {
B* parentPtr;
/* ... */
};

class B {
A child;
/* ... */
};
 
J

Jess

There is another question I forgot to ask. :p

In Effective C++, the author said to decouple declaration and
definitions, we can have two header files for each class: one for
declarations and one for definitions. I'm imagining how they'd look
like and how to use them. I think for the declaration header, perhaps
I can have

//decl.h
class A{
public:
void f();
A();
};

Then in its definition header, it can look like

//def.h
#include "decl.h"
void A::f(){...}
A::A(){...}

Somehow, I don't think this is quite right, because the second one
doesn't look like a header file; it looks like a .cpp file.

There are also two questions that I'm thinking
a. if I need to define some data members in A, then how can I declare
them without showing them in decl.h?
b. for a client of class A, is it "decl.h" that it should #include?

Thanks,
Jess
 
V

Victor Bazarov

Jess said:
There is another question I forgot to ask. :p

In Effective C++, the author said to decouple declaration and
definitions, we can have two header files

<should be>
...two files..
</should be>

Can you find the location in the book where the author says that?
for each class: one for
declarations and one for definitions. I'm imagining how they'd look
like and how to use them. I think for the declaration header, perhaps
I can have

//decl.h
class A{
public:
void f();
A();
};

Then in its definition header,

<should be>
...in its implementation module..
it can look like

//def.h
#include "decl.h"
void A::f(){...}
A::A(){...}

Somehow, I don't think this is quite right, because the second one
doesn't look like a header file; it looks like a .cpp file.
Right.


There are also two questions that I'm thinking
a. if I need to define some data members in A, then how can I declare
them without showing them in decl.h?

"Showing them"? What do you mean by that?
b. for a client of class A, is it "decl.h" that it should #include?

Yes, I would think so.

V
 
J

Jess

<should be>
..two files..
</should be>

Can you find the location in the book where the author says that?

On p144, first paragraph. I'm wondering if the author means we should
use PIMPL strategy. However, he seems to use this item as a general
suggestion.
<should be>
..in its implementation module..
</should be>

Do you mean it should be in .cpp file?

The author says we need a pair of header files, rather than .h + .cpp
files...
"Showing them"? What do you mean by that?

I mean if we only declare a class, then it seems we shouldn't include
implementation detail, hence the data member should be hidden. If the
header file "decl.h" doesn't mention any data member, how can I
secretly define data members in the corresponding implementation file?
I'm now wondering if "decl.h" should only include a declaration of a
class like

class A;
void A::f();

Thanks,
Jess
 
J

James Kanze

Sometimes declarations are all what we need when we define/declare
classes (or functions?), but sometimes we need definitions. I learned
that if we define a class (B) that has an object (a_obj) of a class
type (A), then we need to define A as well, but if B has a pointer to
A, then we only need to forward declare A. I was told this is because
the compiler needs to see the implemenation of A when allocating
memory for a_obj. However, I think compilers won't allocate any
memory until we create an object of class B. Therefore, if I have
a .cpp file
class B{
A a_obj;
};
then it seems ok to forward declare A, because there's no object of
class B created. So in this case, why do I still have to define A (or
include it's header file)?

The need for a definition isn't only related to size; you need a
class definition any time the compiler needs to know more than
just "it's a class". That means anything concerning layout, for
example, which is why you need it here. It also means anytime
you use something declared in the class: a function, a member or
even a typedef. And---because the standard says so---it means
anytime you instantiate a template from the standard library on
the class.
Similarly, we can forward declare A if we only declare a function like
A f(A a);
but we need A's implemenation when we define "f"
A f(A a){...}

Yes, because the function itself has to know how to copy,
construct and destruct the type.
If my .cpp file only has f's definition without any program that calls
"f", then shouldn't it be perfectly safe to forward declare A?

How's the compiler going to generate the destructor of the
argument, or the copy constructor of the return value?
I guess my general question is when can we use declarations without
definitions?

There's a list in the standard (§3.2/4):

A class type T must be complete if:

-- an object of type T is defined (3.1), or

-- a non-static class data member of type T is declared
(9.2), or

-- T is used as the object type or array element type
in a new-expression (5.3.4), or

-- an lvalue-to-rvalue conversion is applied to an
lvalue referring to an object of type T (4.1), or

-- an expression is converted (either implicitly or
explicitly) to type T (clause 4, 5.2.3, 5.2.7,
5.2.9, 5.4), or

-- an expression that is not a null pointer constant,
and has type other than void *, is converted to the
type pointer to T or reference to T using an
implicit conversion (clause 4), a dynamic_cast
(5.2.7) or a static_cast (5.2.9), or

-- a class member access operator is applied to an
expression of type T (5.2.5), or

-- the typeid operator (5.2.8) or the sizeof operator
(5.3.3) is applied to an operand of type T, or

-- a function with a return type or argument type of
type T is defined (3.1) or called (5.2.2), or

-- a class with a base class of type T is defined (10),
or

-- an lvalue of type T is assigned to (5.17).

This is part of a note, and so is non-normative, but I assume
that it is an accurate synthesis of rules that appear spread out
elsewhere throughout the standard.
Another question about forward declaration is that why do we use
instead of
extern class A;
I think the latter says A is a class defined somewhere else.

What does "extern" mean exactly? It means that the name being
defined has external linkage. And the name of a class always
has external linkage (except in block scope). So the extern
here would be superflulous. And if I wrote:
extern class A* pA ;
which name has external linkage: A or pA? (Note that this
statement *is* legal, and it is pA which has external linkage,
and isn't defined; A always has external linkage, and it is the
syntax of what follows which determines whether it is defined or
not.)

A more formal reason is that extern is part of the declaration
specifier, and applies to all of the names specified in the
declarator. When I write "class A;", there is no declarator, so
extern (like other storage class specifiers) is illegal.

The rules aren't completely coherent---in fact, they're not
really very coherent at all. But in this case, they sort of
make sense. Locally, at least, in that allowing extern here
would cause too many other problems elsewhere.

In another language, one might have:

{define|declare} <linkage-spec] definition-or-declaration ;

with everything very explicit. That other language wouldn't
have been based on C, however.
 
J

Jess

Thanks a lot!

The need for a definition isn't only related to size; you need a
class definition any time the compiler needs to know more than
just "it's a class". That means anything concerning layout, for
example, which is why you need it here. It also means anytime
you use something declared in the class: a function, a member or
even a typedef. And---because the standard says so---it means
anytime you instantiate a template from the standard library on
the class.

So if I instantiate a template, say vector, using a class A, then I
need to define A instead of forward declaring A? For the typedef, is
it not enough if I have the following?

class A;

typedef A newAT;
typedef A* ap;

class A{..}

There's a list in the standard (§3.2/4):

A class type T must be complete if:

-- an object of type T is defined (3.1), or

-- a non-static class data member of type T is declared
(9.2), or

-- T is used as the object type or array element type
in a new-expression (5.3.4), or

-- an lvalue-to-rvalue conversion is applied to an
lvalue referring to an object of type T (4.1), or

Can you please tell me what is "lvalue-to-rvalue conversion"?

Thanks,
Jess
 
J

James Kanze

So if I instantiate a template, say vector, using a class A, then I
need to define A instead of forward declaring A?

Yes. At least according to the standard.

Note that the rule is NOT because it is a template. It's
possible to write a template which allows instantiation over an
incomplete type, or at least a class template which allows
instantiation of the class definition (but not necessarily the
member functions) over an incomplete type. But the standard
doesn't require implementations to do this, even if most of the
standard containers probably don't really need the complete type
to instantiate the class (as opposed to member functions like
the constructor).
For the typedef, is
it not enough if I have the following?
typedef A newAT;
typedef A* ap;
class A{..}

For a typedef, yes. For a template, it depends on what's in the
body of the template. For a template in the standard library
(where you don't know what's in the body), no, because the
standard says so, which means that an implementation might do
something in the body which requires a complete type.
Can you please tell me what is "lvalue-to-rvalue conversion"?

Ouch. That's not easy to answer in the limited space of a
posting, at least not without knowing how much you already know.
As a first approximation, basically, an lvalue is an expression
which refers to a specific object, like the name of a variable,
or the results of dereferencing a pointer. An rvalue is an
expression which is not an lvalue---it's just a value, without
any underlying object. (This is very close to the absolute
truth for built-in types. It gets a bit more complicated when
class types are taken into consideration.) An lvalue-to-rvalue
conversion occurs whenever you have an lvalue, and need an
rvalue. For example:

int x ;

x = 3 ; // x is used as an lvalue...
std::cout << x ; // x is used as an rvalue...

In the above example, the 'x' is an lvalue. The output operator
needs a simple rvalue. The conversion of the lvalue 'x' to an
rvalue results in the value 3.

In classical C, an lvalue designated an object, an rvalue
didn't. In the above, x is an object, 3 isn't; in the
assignment, we needed an lvalue on the left, since we need an
object in which to put the 3. An expression like "3 = x"
doesn't work.

It's hard to find an example of code where you might require an
lvalue to rvalue conversion with an incomplete type, however.
Class types (which I'll talk about later) are a bit special, and
the only non-class incomplete type is void. About the only
example I can think of is:

void* p1 ;
void* p2 ;
*p1 = *p2 ;

The right hand side of an assignment is an rvalue, and since
void is an incomplete type, the rvalue to lvalue conversion
fails. (But there is another rule which says that the target
type of a conversion must be complete, and that's enough to make
the above assignment illegal.)

With class types, it's almost impossible to talk reasonably
about lvalue-to-rvalue conversions, because all operations on
classes are defined as function calls, and a function call
doesn't require an lvalue-to-rvalue conversion, ever: you can
call a member function on an lvalue, you can bind an lvalue to a
refernece, and in all other cases, you use the copy constructor
to construct a new instance (and you can copy an lvalue). On
the other hand, you can't call any member function (including
the copy constructor or the assignment operator) unless the type
is complete, which is what makes things like:
class C ;
C c1, c2 ;
c1 = c2 ;
illegal. (The distinction lvalue-rvalue is still relevant for
class types, since you cannot bind an rvalue to a non-const
reference.)
 
J

Jess

Yes. At least according to the standard.

Note that the rule is NOT because it is a template. It's
possible to write a template which allows instantiation over an
incomplete type, or at least a class template which allows
instantiation of the class definition (but not necessarily the
member functions) over an incomplete type. But the standard
doesn't require implementations to do this, even if most of the
standard containers probably don't really need the complete type
to instantiate the class (as opposed to member functions like
the constructor).


For a typedef, yes. For a template, it depends on what's in the
body of the template. For a template in the standard library
(where you don't know what's in the body), no, because the
standard says so, which means that an implementation might do
something in the body which requires a complete type.


Ouch. That's not easy to answer in the limited space of a
posting, at least not without knowing how much you already know.
As a first approximation, basically, an lvalue is an expression
which refers to a specific object, like the name of a variable,
or the results of dereferencing a pointer. An rvalue is an
expression which is not an lvalue---it's just a value, without
any underlying object. (This is very close to the absolute
truth for built-in types. It gets a bit more complicated when
class types are taken into consideration.) An lvalue-to-rvalue
conversion occurs whenever you have an lvalue, and need an
rvalue. For example:

int x ;

x = 3 ; // x is used as an lvalue...
std::cout << x ; // x is used as an rvalue...

In the above example, the 'x' is an lvalue. The output operator
needs a simple rvalue. The conversion of the lvalue 'x' to an
rvalue results in the value 3.

In classical C, an lvalue designated an object, an rvalue
didn't. In the above, x is an object, 3 isn't; in the
assignment, we needed an lvalue on the left, since we need an
object in which to put the 3. An expression like "3 = x"
doesn't work.

It's hard to find an example of code where you might require an
lvalue to rvalue conversion with an incomplete type, however.
Class types (which I'll talk about later) are a bit special, and
the only non-class incomplete type is void. About the only
example I can think of is:

void* p1 ;
void* p2 ;
*p1 = *p2 ;

The right hand side of an assignment is an rvalue, and since
void is an incomplete type, the rvalue to lvalue conversion
fails. (But there is another rule which says that the target
type of a conversion must be complete, and that's enough to make
the above assignment illegal.)

With class types, it's almost impossible to talk reasonably
about lvalue-to-rvalue conversions, because all operations on
classes are defined as function calls, and a function call
doesn't require an lvalue-to-rvalue conversion, ever: you can
call a member function on an lvalue, you can bind an lvalue to a
refernece, and in all other cases, you use the copy constructor
to construct a new instance (and you can copy an lvalue). On
the other hand, you can't call any member function (including
the copy constructor or the assignment operator) unless the type
is complete, which is what makes things like:
class C ;
C c1, c2 ;
c1 = c2 ;
illegal. (The distinction lvalue-rvalue is still relevant for
class types, since you cannot bind an rvalue to a non-const
reference.)

Many thanks for the explanations! For class types, what are rvalues?
Are they the (temporary) return values from functions?
Jess
 
J

James Kanze

On Jun 21, 1:48 am, James Kanze <[email protected]> wrote:
For class types, what are rvalues?

Formally, the same rules as to whether an expression is an
lvalue or an rvalue apply to both class types and non-class
types. In practice, of course, most of the expressions aren't
legal on a class type (except in the case of overloaded
operators, and then the rules that apply are those of a function
call, not of the operator). In fact, I think that the only way
to get a class type rvalue is to call a function or if there is
a conversion (explicit or implicit): a function call or the
results of a conversion are lvalues if and only if the return
type is a reference.

Note that implicit conversions resulting from constructors which
take a single argument or from user defined conversion operators
are rvalues (except, in the latter case, if the conversion is to
a reference). And don't forget that the standard considers
something like "MyClass()" or "MyClass(a,b,c)" a conversion,
even if it is hard to understand exactly what is being
converted---at any rate, they are also rvalues.
Are they the (temporary) return values from functions?

Yes. In many ways, temporary and rvalue are synonyms. The
tendency is to use rvalue when we are concerned about
constraints (e.g. on arguments to certain operators, or for
binding to a reference), and temporary when we are concerned
with the object itself, in particular its lifetime.
 
J

Jess

Formally, the same rules as to whether an expression is an
lvalue or an rvalue apply to both class types and non-class
types. In practice, of course, most of the expressions aren't
legal on a class type (except in the case of overloaded
operators, and then the rules that apply are those of a function
call, not of the operator). In fact, I think that the only way
to get a class type rvalue is to call a function or if there is
a conversion (explicit or implicit): a function call or the
results of a conversion are lvalues if and only if the return
type is a reference.

Note that implicit conversions resulting from constructors which
take a single argument or from user defined conversion operators
are rvalues (except, in the latter case, if the conversion is to
a reference). And don't forget that the standard considers
something like "MyClass()" or "MyClass(a,b,c)" a conversion,
even if it is hard to understand exactly what is being
converted---at any rate, they are also rvalues.


Yes. In many ways, temporary and rvalue are synonyms. The
tendency is to use rvalue when we are concerned about
constraints (e.g. on arguments to certain operators, or for
binding to a reference), and temporary when we are concerned
with the object itself, in particular its lifetime.

--
James Kanze (GABI Software, from CAI) email:[email protected]
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

Thanks again! Then can I generalize it to say that we can't assign
values to class-typed rvalues because the rvalues are temporaries and
so compilers don't allow us to assign anything to temporaries?

Jess
 
J

James Kanze

Thanks again! Then can I generalize it to say that we can't assign
values to class-typed rvalues because the rvalues are temporaries and
so compilers don't allow us to assign anything to temporaries?

No. In fact, you can assign to a class type temporary. All of
the rules concerning where lvalues are required or not concern
the built-in operators, and a class type doesn't have any
built-in operators. User defined operators obey the rules for
function calls (since that's what they are), and you are allowed
to call a member function on an rvalue/temporary. So, given
something like:

f() = x ;

This is legal if f returns a reference (because a reference is
always an lvalue), or if f returns a class type, because
operator= is a member function, and you can call member
fucntions on class types.

(Of course, the types have to match. And if the return type of
f is const, then you can't call non-const functions on it, and
since operator= is always a non-const function, that prevents
it.)
 
J

Jess

No. In fact, you can assign to a class type temporary. All of
the rules concerning where lvalues are required or not concern
the built-in operators, and a class type doesn't have any
built-in operators. User defined operators obey the rules for
function calls (since that's what they are), and you are allowed
to call a member function on an rvalue/temporary. So, given
something like:

f() = x ;

This is legal if f returns a reference (because a reference is
always an lvalue), or if f returns a class type, because
operator= is a member function, and you can call member
fucntions on class types.

(Of course, the types have to match. And if the return type of
f is const, then you can't call non-const functions on it, and
since operator= is always a non-const function, that prevents
it.)

--
James Kanze (Gabi Software) email: (e-mail address removed)
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

Thanks for clarifying the matter!
Jess
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top