No object definitions in header files?

V

Victor Bazarov

Alf said:
* Andrey Tarasevich:

Unless those objects are static members of some template class.

But then they are not objects but only object templates. Remember,
a member of a class template *is* a template.

V
 
D

Daniel T.

zouyongbin said:
Stanley B Lippman in his "C++ Primer" that a definition like this
should not appear in a header file:

int ix;
Correct.

The inclusion of any of these definitions in two or more files of
the same program will result in a linker error complaining about
multiple definitions. So this kind of definition should be avoided
as much as possible. But as we know, the definition of a class is
always in a header file.

All true.
And we can use "#ifndef" to eliminate the linker error complaining
about multiple definitions.

Not correct. The use of include guards is not to eliminate linker
errors, it is to eliminate compiler errors.
Do you agree with Mr Lippman?

Yes, I agree with Mr. Lippman.
I thought that the definition of a object is the same with the
definition of a class for header files.

No, the definition of an object is not the same as a definition of a
class. As long as all the class definitions are alike, multiple
definitions of a class will not cause a linker error, whereas multiple
definitions of an object will.

This really ought to be an FAQ:

"""
As a rule of thumb, a header may contain:
Named namespaces
namespace N { /*...*/ }
Type definitions
struct Point { int x, y; };
Template declarations
template<class T> class Z;
Template definitions
template<class T> class V { /* ... */ };
Function declarations
extern int strlen(const char*);
Inline function definitions
inline char get(char* p) { return *p++; }
Data declarations
extern int a;
Constant definitions
const float pi = 3.141593;
Enumerations
enum Light { red, yellow, green };
Name declarations
class Matrix;
Include directives
#include <algorithm>
Macro definitions
#define VERSION 12
Conditional compilation directives
#ifdef __cplusplus
Comments
/* check for end of file */

....

Conversely, a header should never contain:
Ordinary function definitions
char get(char* p) { return *p++; }
Data definitions
int a;
Aggregate definitions
short tbl[] = { 1, 2, 3 };
Unnamed namespaces
namespace { /* ... */ }
Exported template definitions
export template<class T> f(T t) { /* ... */ }
"""
--The C++ Programming Language 3rd Ed., Bjarne Stroustrup, section 9.2.1

To that second list one should add using declarations and directives.
using namespace foo;
using foo::bar;

--C++ Coding Standards, Herb Sutter &al,
 
K

Kaz Kylheku

zouyongbin said:
Stanley B Lippman in his "C++ Primer" that a definition like this
should not appear in a header file:

int ix;

That is a guideline, not a rule. Guidelines can be broken.

Firstly, a static definition can certainly appear in a header.

static int x = 3;

This will create a private x in every translation unit that includes
it.

Secondly, you can use careful #if or #ifdef guards to ensure that the
one definition rule is observed, even if you have external definitions
in header files.

This can be exploited as follows:

// A.h
#ifndef A_H_34DF_C91B
#define A_H_34DF_C91B

class A {
...
};

#ifdef GENERATE_DEFS
A global_instance_a;
#else
extern A global_instance_a;
#endif

#endif

#endif
// End of A.h



// B.h
#ifndef B_H_37D0_1031
#define B_H_37D0_1031

#include "A.h"

class B {
... uses class A in some way
};

#ifdef GENERATE_DEFS
B global_instance_b(global_instance_a);
#else
extern B global_instance_b;
#endif

// End of B.h

Here we have two headers, A.h and B.h. They respectively define classes
A and B. Normally, the GENERATE_DEFS symbol is not defined and so the
headers provide an external declaration for objects global_instance_a
and global_instance_b.

The global_instance_b object knows about global_instance_a; it uses it,
and therefore global_instance_a must be initialized before
global_instance_b.

You create some source file where you do this:

#define GENERATE_DEFS
#include "B.h"

Now this becomes a translation unit in which all of the dependent
definitions appear, and in a sequence which ensures their correct
initialization order.
But as we know, the definition of a class is always in a
header file.

No, that is a class declaration, not a definition.
And we can use "#ifndef" to eliminate the linker error
complaining about multiple definitions.

No, the #ifdef eliminates multiple declarations of the same class in
the same translation unit. It does not prevent different translation
units from including that class.

This is different from the restriction on an external definition, which
must be made in exactly one place in the entire program.

The way many C++ implementations work internally, a duplicate class
declaration in the same translation unit is caught and diagnosed by the
compiler. Duplicate external definitions made in separate translation
units are caught by the linker.

You can actually have multiple declarations of an object in the same
(file) scope, but only one definition. E.g.

int x;
extern int x;
int x;
int x = 3;

This is a valid translation unit.

You can also have multiple declarations of a class, albeit only one of
them can provide a body, e.g:

class A;

class A {
...
};
 
A

Alf P. Steinbach

* Victor Bazarov:
But then they are not objects but only object templates. Remember,
a member of a class template *is* a template.

Well, no.

E.g., think about the header

template< Dummy_ >
struct Snark_ { static double globalTemperature; };

template< Dummy_ >
double Snark_<Dummy_>::globalTemperature = 3.333;

typedef Snark_<void> Snark; // External linkage.

double const tempSnapshot
= Snark::globalTemperature; // Internal linkage, using above.

Here Snark::globalTemperature is an external linkage variable of type
double defined in the header.
 
B

bjarne

Daniel T. wrote:

"what can go into a header file?"
This really ought to be an FAQ:


Yo are right. It is a frequently asked question, so it should be in
FAQs.

...
Conversely, a header should never contain:
Ordinary function definitions
char get(char* p) { return *p++; }
Data definitions
int a;
Aggregate definitions
short tbl[] = { 1, 2, 3 };
Unnamed namespaces
namespace { /* ... */ }
Exported template definitions
export template<class T> f(T t) { /* ... */ }
"""
--The C++ Programming Language 3rd Ed., Bjarne Stroustrup, section 9.2.1

To that second list one should add using declarations and directives.
using namespace foo;
using foo::bar;

I think that using declarations are often suitable in a header, to
selectively make names available. However, putting a using directive in
a header is as stated) most likely going to cause problems because it
would include an unbounded set of names (that would change and could
cause problems if the namespace later changed).

-- Bjarne Stroustrup; http://www.research.att.com/~bs
 
D

Daniel T.

Alf P. Steinbach said:
* Daniel T.:

Are you sure that Stanley really wrote that? Do you have a quote?

I'm not sure if Mr. Lippman said it or not, but the statement is
non-the-less correct. Data definitions should not be put in header files
per Stroustrup (I've already posted the quote for that, but you snipped
it.)
You mean, all false.

Hmm, it is true that multiple inclusions of data definitions will cause
a linker error complaining about multiple definitions. (The OP's first
sentence above.) It is true that this kind of definition should be
avoided in header files. (The OP's second sentence above.) And though
the definition of a class (what Stroustrup calls a "type definition" in
the quote I posted,) isn't *always* in a header file, it often is.

So each individual statement are basically true, AFAICT.
 
D

Daniel T.

bjarne said:
I think that using declarations are often suitable in a header, to
selectively make names available. However, putting a using directive
in a header is as stated) most likely going to cause problems
because it would include an unbounded set of names (that would
change and could cause problems if the namespace later changed).

Though I'm antsy to contradict you, I will quote Sutter and Alexandrescu:

Many people think that using declarations issued at namespace
level (for example, using N::Widget;) are safe. They are not. They
are at least as dangerous, and in a subtler and more insidious
way. Consider:

// snippet 1
namespace A {
int f(double);
}

// snippet 2
namespace B {
using A::f;
void g();
}

// snippet 3
namespace A {
int f(int);
}

// snippet 4
void B::g() {
f(1); // which overload is called?
}

... So, from within B, which overloads are visible depends on
where these code snippets exist and in what order they are
combined.
 
A

Alf P. Steinbach

* Daniel T.:
I'm not sure if Mr. Lippman said it or not, but the statement is
non-the-less correct. Data definitions should not be put in header files
per Stroustrup (I've already posted the quote for that, but you snipped
it.)

Yes, Bjarne wrote a list of rules-of-thumb.

Those are good rules for newbies, or rules of thumb for newbies.

The OP's statement is a bit more absolute regarding where it applies
(namely universally in header files), and a bit more vague about what it
applies to, and is therefore highly questionable.

Hmm, it is true that multiple inclusions of data definitions will cause
a linker error complaining about multiple definitions. (The OP's first
sentence above.)

The OP's statement quoted right above is false. In particular the
definition 'int i;' is extremely common, and may occur thousands or tens
of thousands of times in a large application. No ill effect as long as
those are not definitions of the same name denoting the same kind of
thing in the same namespace.

Your statement is even more false, if that is possible.

I've illustrated elsethread one way to do direct variable definitions at
namespace scope properly in a header file (just insert missing
'typename' keywords in that code), so that the header file can be safely
included in any number of translation units. By that example, it's
false that multiple inclusions of data definitions from a header file
will necessarily cause any kind of error in a conforming C++
implementation. If your compiler or linker complains for that example,
then it's not standard-conforming.

It is true that this kind of definition should be
avoided in header files. (The OP's second sentence above.)

First, it's not true that the kind of definition illustrated should be
avoided in a header file. But it's a good idea to avoid it at namespace
scope. Second, it's not true that that was the OP's second sentence,
and it's not true that your paraphrasing above was the OP's second sentence.

And though
the definition of a class (what Stroustrup calls a "type definition" in
the quote I posted,) isn't *always* in a header file, it often is.

Good that you admit that your "all true" isn't true after all.

So each individual statement are basically true, AFAICT.

But this leap of logic is beyond me.
 
D

Daniel T.

Alf P. Steinbach said:
* Daniel T.:

Yes, Bjarne wrote a list of rules-of-thumb.

Those are good rules for newbies, or rules of thumb for newbies.

The OP's statement is a bit more absolute regarding where it applies
(namely universally in header files), and a bit more vague about
what it applies to, and is therefore highly questionable.

It seems pretty spicific as to what it applies to. It applies to putting
data definitions (i.e., "int i;") at global or namespace scope in header
files that are included in multiple translations units. I'm willing to
say that I may be reading more into it.
The OP's statement quoted right above is false. In particular the
definition 'int i;' is extremely common, and may occur thousands or tens
of thousands of times in a large application. No ill effect as long as
those are not definitions of the same name denoting the same kind of
thing in the same namespace.

OK, I see the disconnect now. You are assuming that the OP is
questioning putting "int i;" *anywhere* in a header file. I didn't think
the OP was trying to make that blanket of a statement. It seemed to me
he was more confused as to why a data definition should not be put at
the global or namespace scope of a .h file whereas newbies are
encouraged to put a class definition at the same scope in the .h file.
I've illustrated elsethread one way to do direct variable
definitions at namespace scope properly in a header file (just
insert missing 'typename' keywords in that code), so that the header
file can be safely included in any number of translation units.

The only example I noticed was the Dummy Snark example and it didn't
have any data definitions in it. It did have a constant defintion in it
(which Stroustrup puts on the list of things that can safely go in
header files.) The OP wasn't talking about constant definitions.
 
A

Alf P. Steinbach

* Daniel T.:
The only example I noticed was the Dummy Snark example and it didn't
have any data definitions in it. It did have a constant defintion in it
(which Stroustrup puts on the list of things that can safely go in
header files.) The OP wasn't talking about constant definitions.

Note that Snark::globalTemperature is an extern linkage /non-constant/
variable, defined and initialized to a specific value in the header
file, where that header file can be included in any number of
translation units without ill effects.

OK, there was a typo in that off-the-cuff example (missing 'typename'),
and also a typo in the paragraph you quoted above (the variable is not
directly at namespace scope, but is still a global), and the reason I
defined a non-const variable was just to show it could be done: it's
generally Very UnGood to define global non-const variables.

But repeating such a non-const variable definition in different
translation units in the way I showed, does absolutely not require any
linker error or other diagnostic as you maintained it must: it's
standard C++ which a conforming implementation must accept.
 
A

Andrey Tarasevich

Daniel said:
...
This really ought to be an FAQ:
...
--The C++ Programming Language 3rd Ed., Bjarne Stroustrup, section 9.2.1
...

Bjarne Stroustrup's books are often aimed at beginners and as such, they
often took certain liberties with C++ terminology for the sake of
simplicity. This group nomally prefers to stick to the official
terminology used in the language standard. That applies to the FAQ as
well, which means that the quote you provided is not really ready to be
placed in the FAQ just yet.
 
A

Andrey Tarasevich

Daniel said:
Though I'm antsy to contradict you, I will quote Sutter and Alexandrescu:

Many people think that using declarations issued at namespace
level (for example, using N::Widget;) are safe. They are not. They
are at least as dangerous, and in a subtler and more insidious
way.
...

Sorry, but that's not convincing enough to serve as an argument against
placing such declarations in header files. There's a huge difference
between something being "safe" and something being "practically useful".
After all, there nothing in C++ that's absolutely "safe".
 
D

Daniel T.

Andrey Tarasevich said:
Sorry, but that's not convincing enough to serve as an argument
against placing such declarations in header files. There's a huge
difference between something being "safe" and something being
"practically useful". After all, there nothing in C++ that's
absolutely "safe".

Well, since it is not my argument I don't think I will carry it any
further. For my own part, I find that code which depends on order of
compilation is harder to understand and maintain, so I will continue to
avoid such code.
 
K

Krishanu Debnath

Kaz Kylheku wrote:
You can actually have multiple declarations of an object in the same
(file) scope, but only one definition. E.g.

int x;
extern int x;
int x;
int x = 3;

This is a valid translation unit.

I know above is valid C. But C++? Can you please provide the C&V.

Krishanu
 
G

Greg

Krishanu said:
Kaz Kylheku wrote:


I know above is valid C. But C++? Can you please provide the C&V.

The statement is mostly correct - a single variable may appear any
number of times in an (extern) variable declaration but only once in a
variable definition. The example, however, shows exactly the opposite:
three definitions of an x variable and only one x extern declaration.

So swapping the x declaration with the x definitions does create an
example of legal C++:

extern int x;
int x;
extern int x;
extern int x;

Greg
 
A

Andrey Tarasevich

Alf said:
Well, no.

E.g., think about the header

template< Dummy_ >
struct Snark_ { static double globalTemperature; };

template< Dummy_ >
double Snark_<Dummy_>::globalTemperature = 3.333;

typedef Snark_<void> Snark; // External linkage.

double const tempSnapshot
= Snark::globalTemperature; // Internal linkage, using above.

Here Snark::globalTemperature is an external linkage variable of type
double defined in the header.
...

Not really. (The 'typedef' does not really add anything - it's just a piece of
syntactic sugar that doesn't cause the instantiation of the template, so we can
get rid of it without changing the effect of this example.) What we really have is

double const tempSnapshot = Snark_<void>::globalTemperature;

The initializer expression in this case in a _specialization_ of a template.
This specialization is used in a context that will cause the data member
template to be _implicitly_ _instantiated_. That instantiation is what turns the
data member template into an actual (i.e. storage-occupying) object. In C++
terminology, 'implicit instantiation' is a process different from 'definition',
even though they have certain similarities. 'Definition' is something the user
explicitly does himself. 'Implicit instantiation' is something the compiler does
behind the scenes. Even though this implicit instantiation will "materialize"
this data member, it is still not correct to assume that it works "as-if" this
'definition' occurs specifically in this header file.

If you wanted to perform instantiation of that data member that is indeed very
similar to definition, you'd have to do an _explicit_ instantiation

template double Snarl_<void>::globalTemperature; // explicit instantiation

The effect of the above is indeed a close equivalent of 'defining' a static data
member of a class template. But if done in a header file, this will (not
surprisingly) lead to multiple-definition error, just like an object definition
would.

--
Best regards,
Andrey Tarasevich

P.S. BTW, your example, if fact, is similar to this:

template< typename Dummy_ >
struct Snark_ { static void foo(); };

template< typename Dummy_ >
void Snark_<Dummy_>::foo() {} // external linkage

static void bar() // internal linkage
{ Snark_<void>::foo(); }

According to your logic, in this case we also have a definition of static
function 'Snark_<void>::foo' with external linkage in header file. So,
technically you had to include static member functions in your "unless"
statement as well.
 

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,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top