The ODR thing again

  • Thread starter Steven T. Hatton
  • Start date
S

Steven T. Hatton

If I have a non-inline function definition in a header file with header
guards like this:
#ifndef HEADER_GUARD_H
#define HEADER_GUARD_H

int foo(){ return 42; }
namespace { bar() { return 666; }
#endif

that definition of foo() will only appear once in any given translation
unit, correct? "Translation unit" is a synonym for "file scope", is it
not? Am I correct that the implementation is not required to detect ODR
violations across TUs? Is it, therefore, /possible/ that a conforming
compiler could compile code where foo() is defined in multiple TUs?

My understanding of the Standard is that names within an unnamed namespace
have file scope. Does that mean I should be able to define bar() in an
unnamed namespace and never get an ODR violation from the file with the
definition (within header guards) being #included in in multiple TUs within
the same program?
 
A

Alf P. Steinbach

* Steven T. Hatton:
If I have a non-inline function definition in a header file with header
guards like this:
#ifndef HEADER_GUARD_H
#define HEADER_GUARD_H

int foo(){ return 42; }
namespace { bar() { return 666; }
#endif

that definition of foo() will only appear once in any given translation
unit, correct?

If the header file is used as intended.

"Translation unit" is a synonym for "file scope", is it not?

No.

In practical terms, a translation unit is comprised of the program text
that results from preprocessing of one C++ source file.

File scope refers to declarations outside any class or function.

Am I correct that the implementation is not required to detect ODR
violations across TUs?
Yes.


Is it, therefore, /possible/ that a conforming
compiler could compile code where foo() is defined in multiple TUs?

Yes (to make the code correct, declare the function as 'inline', that's
what that keyword is for).

My understanding of the Standard is that names within an unnamed namespace
have file scope.

They have namespace scope, which is what's usually meant by "file scope".

The "unnamed" namespace is not really unnamed but has an automatically
generated unique name for that translation unit.

Names defined in that namespace are then imported to the global
namespace as if

namespace{ void bar(){} }

is translated to

namespace veryUniqueName{ void bar(){} }

using namespace veryUniqueName;

which you may recognize from having done just about the same for
namespace std (at least, most students do).

Does that mean I should be able to define bar() in an
unnamed namespace and never get an ODR violation from the file with the
definition (within header guards) being #included in in multiple TUs within
the same program?

bar() will not violate the ODR, while foo will.
 
S

Steven T. Hatton

Alf said:
* Steven T. Hatton:

No.

In practical terms, a translation unit is comprised of the program text
that results from preprocessing of one C++ source file.

So, in terms of compiling a C++ program "file" means the source file, and
everything #included directly or indirectly in the source file. This is
also called a translation unit.
File scope refers to declarations outside any class or function.

Is there any scope in a translation unit which is not coincident with file
scope?
Yes (to make the code correct, declare the function as 'inline', that's
what that keyword is for).

To be pedantic, is it not more correct to say that the storage class
specifier 'static' used at file scope is intended for that purpose? I
suspect both inline and static will have the same effect in most cases. The
one place in which I found the use of an unnamed namespace valuable was not
involving functions, so I probably should have avoided using a function in
the is example. Where I found it useful was in defining const char[]
variables to pass as template parameters.
They have namespace scope, which is what's usually meant by "file scope".

The "unnamed" namespace is not really unnamed but has an automatically
generated unique name for that translation unit.

Names defined in that namespace are then imported to the global
namespace as if

namespace{ void bar(){} }

is translated to

namespace veryUniqueName{ void bar(){} }

using namespace veryUniqueName;

which you may recognize from having done just about the same for
namespace std (at least, most students do).

If by student you mean a person who reads a book on how to program in C++,
and teacher you mean an author of such a book, I will say that the use
of 'using namespace' directives is far more common in code written by
teachers than it is, or ever has been, in my code. When I started trying
to learn C++ in earnest I began with outdated texts that used <iostream.h>
and put std::cout in the global namespace. When I compiled the code, I got
warning messages telling me my code was using deprecated functionality.

That made me very frustrated and angry. I asked people for help, and they
told me how to contaminate the global namespace with a `using' directive.
It seemed rather pointless to have this convoluted feature which seemed to
serve no other purpose than to intimidate the novice. I therefore
investigated the proper meaning and intent of namespaces.
bar() will not violate the ODR, while foo will.

How do the use of `inline', `static', and unnamed namespaces differ in their
effect? That is, other than the fact that `inline' is not applicable to
variables.

Sutter and Alexandrescu advise against all of these. If I understand
correctly, this is because it leads to redundant instances of object code
being placed on the stack at runtime.

And then there's extern...
 
A

Alf P. Steinbach

* Steven T. Hatton:
* Alf P. Steinbach:
Names defined in [an anonymous namespace] are then imported to the global
namespace as if

namespace{ void bar(){} }

is translated to

namespace veryUniqueName{ void bar(){} }

using namespace veryUniqueName;

which you may recognize from having done just about the same for
namespace std (at least, most students do).

If by student you mean a person who reads a book on how to program in C++,
and teacher you mean an author of such a book, I will say that the use
of 'using namespace' directives is far more common in code written by
teachers than it is, or ever has been, in my code.

Sorry, I meant no disrespect.


[snip]
How do the use of `inline', `static', and unnamed namespaces differ in their
effect? That is, other than the fact that `inline' is not applicable to
variables.

Sutter and Alexandrescu advise against all of these. If I understand
correctly, this is because it leads to redundant instances of object code
being placed on the stack at runtime.

And then there's extern...

This has to do with linkage and the One Definition Rule (ODR).

Linkage: which names are matched (bound) to which declarations, across
translation units. A pure declaration of a name N must be matched to a
definition of N if N is used at run-time. An example where N is only
used at compile time and so needs no definition: sizeof(N).

There is external linkage, internal linkage and no linkage.

When a name N has external linkage a pure declaration of N can be
matched to a definition in another translation unit. The ODR: unless N
is (a) declared 'inline' or (b) is a template specialization, there must
not be more than one definition. In the exceptional cases (a) and (b),
which are really the same case but has no general C++ desciptive term,
if there is more than one definition they must all be the same (but no
diagnostic required for violation), which means in practice that the
linker can just pick the first definition it encounters and discard the
rest, which is the reason that at the tool level cases (a) and (b)
multiple definitions are known as "discardable" records.

When a name N has internal linkage a pure declaration of N can only be
matched to a definition in the same translation unit. This means the
name N can be used, differently or in the same way, in any number of
translation units. There are two ways to get internal linkage: 'static'
and 'const'; 'static' used for this purpose (it has far too many
meanings!) is deprecated for data but not for functions.

When a name N has no linkage a pure declaration of N isn't matched to
any definition. That means that if N is used at run time it can't be
just declared and defined elsewhere, it must be defined in the
declaration. A local class has no linkage.

The main point of an anonymous namespace is to keep external linkage (so
that e.g. the template mechanism can be employed, because actual
template parameters must have external linkage) but provide unique
per-translation unit names so as to avoid name conflicts across
translation units.

I doubt that Herb and Andrei advice against 'inline', 'static' and
anonymous namespaces in general.
 
S

Steven T. Hatton

Alf said:
* Steven T. Hatton:

This has to do with linkage and the One Definition Rule (ODR).

Linkage: which names are matched (bound) to which declarations, across
translation units. A pure declaration of a name N must be matched to a
definition of N if N is used at run-time. An example where N is only
used at compile time and so needs no definition: sizeof(N).

There is external linkage, internal linkage and no linkage.

When a name N has external linkage a pure declaration of N can be
matched to a definition in another translation unit. The ODR: unless N
is (a) declared 'inline' or (b) is a template specialization, there must
not be more than one definition. In the exceptional cases (a) and (b),
which are really the same case but has no general C++ desciptive term,
if there is more than one definition they must all be the same (but no
diagnostic required for violation), which means in practice that the
linker can just pick the first definition it encounters and discard the
rest, which is the reason that at the tool level cases (a) and (b)
multiple definitions are known as "discardable" records.

When a name N has internal linkage a pure declaration of N can only be
matched to a definition in the same translation unit. This means the
name N can be used, differently or in the same way, in any number of
translation units. There are two ways to get internal linkage: 'static'
and 'const'; 'static' used for this purpose (it has far too many
meanings!) is deprecated for data but not for functions.

When a name N has no linkage a pure declaration of N isn't matched to
any definition. That means that if N is used at run time it can't be
just declared and defined elsewhere, it must be defined in the
declaration. A local class has no linkage.

The main point of an anonymous namespace is to keep external linkage (so
that e.g. the template mechanism can be employed, because actual
template parameters must have external linkage) but provide unique
per-translation unit names so as to avoid name conflicts across
translation units.

I doubt that Herb and Andrei advice against 'inline', 'static' and
anonymous namespaces in general.

Wanna bet?

Ironically, when I looked more closely at the section "Do not define
entities with linkage in a header file", I saw the the way of getting
around that is to use `extern'. I haven't tried it yet, but I believe I
now understand why `extern' never worked for me. I was trying to define
the entity in /one/ of the header files, rather than in an implementation
file.
 
A

Alf P. Steinbach

* Steven T. Hatton:
Wanna bet?

No, I see you: quote that advice if you think it exists.

Ironically, when I looked more closely at the section "Do not define
entities with linkage in a header file", I saw the the way of getting
around that is to use `extern'.

Then you have misunderstood.
 
S

Steven T. Hatton

Alf said:
* Steven T. Hatton:

No, I see you: quote that advice if you think it exists.

In the section titled "Do not define entities with linkage in a header
file", they don't explicitly advise against header files with inline
functions. OTOH, they do not propose the use of `inline' as a means of
defining functions arbitrarily in a header file. Functions that already
have a reason to be inline /do/ belong in a header file.
Then you have misunderstood.
<quote>
The solution is simple - put just the declaration in a header:

extern int fudgeFactor;
extern string hello;
void foo(); //extern for functions is optional

The actual definitions go in a single implementation file:

int fudgeFactor;
string hello("Hello, world!");
void foo(){/*...*/}
</quote>
 
A

Alf P. Steinbach

* Steven T. Hatton:
In the section titled "Do not define entities with linkage in a header
file", they don't explicitly advise against header files with inline
functions. OTOH, they do not propose the use of `inline' as a means of
defining functions arbitrarily in a header file. Functions that already
have a reason to be inline /do/ belong in a header file.

<quote>
The solution is simple - put just the declaration in a header:

extern int fudgeFactor;
extern string hello;
void foo(); //extern for functions is optional

The actual definitions go in a single implementation file:

int fudgeFactor;
string hello("Hello, world!");
void foo(){/*...*/}
</quote>

I don't understand what you think, what the misconceptions are.

The above is meaningless to me.

I and others will be happy to answer simple questions.
 
S

Steven T. Hatton

Alf said:
* Steven T. Hatton:

I don't understand what you think, what the misconceptions are.

The above is meaningless to me.

It is directly quoted from _C++ Coding Standards_ page 112.
I and others will be happy to answer simple questions.

What have I misunderstood?
 
A

Alf P. Steinbach

* Steven T. Hatton:
It is directly quoted from _C++ Coding Standards_ page 112.

The quote is (I take your word for that) but not the quote's context
here, as a solution to... something.

What have I misunderstood?

That's the question, yes.

I don't understand what the misconceptions or lacking understanding
might be. I'm baffled, befuddled, perplexed etc. by the above. It's
possible that the authors are presenting a rule-of-thumb that mostly
helps newbies to avoid violating the ODR. But then there should have
been discussion of 'inline' and 'template', which are the proper devices
to use for avoiding ODR violations when one wants or needs to define
entities with linkage in header files (surely /Andrei/, the master of
templates, cannot be advicing to not put template definitions, which
have linkage, in header files). Are you sure the section's title is
quoted correctly and entirely, and that there's no such discussion?
 
S

Steven T. Hatton

Alf said:
* Steven T. Hatton:

The quote is (I take your word for that) but not the quote's context
here, as a solution to... something.



That's the question, yes.

I don't understand what the misconceptions or lacking understanding
might be. I'm baffled, befuddled, perplexed etc. by the above. It's
possible that the authors are presenting a rule-of-thumb that mostly
helps newbies to avoid violating the ODR. But then there should have
been discussion of 'inline' and 'template', which are the proper devices
to use for avoiding ODR violations when one wants or needs to define
entities with linkage in header files (surely /Andrei/, the master of
templates, cannot be advicing to not put template definitions, which
have linkage, in header files).

Well, obviously, without the availability of `export' in C++ a
implementation, one cannot put template definitions in implementation files
following the conventions used for non-template constructs. Yes, they make
exceptions for those. They also make exceptions for inline functions. The
reason to declare a function inline is not, however, to avoid ODR
violations. It is to take advantage of the performance improvements
available when the compiler has the entire definition at its disposal, etc.
The ODR immunity is a consequence of the design, not the motive for it.
Are you sure the section's title is
quoted correctly and entirely,

Yes.

http://www.gotw.ca/publications/c++cs.htm
61. Don?t define entities with linkage in a header file. 112
and that there's no such discussion?

I never said there is no such discussion. I will say, however, that the
argument against entities with linkage in header files goes beyond avoiding
ODR violations.
 
A

Alf P. Steinbach

* Steven T. Hatton:
The
reason to declare a function inline is not, however, to avoid ODR
violations. It is to take advantage of the performance improvements
available when the compiler has the entire definition at its disposal, etc.
The ODR immunity is a consequence of the design, not the motive for it.

That, you have completely misunderstood. ;-)

Yes.

http://www.gotw.ca/publications/c++cs.htm
61. Don?t define entities with linkage in a header file. 112


I never said there is no such discussion. I will say, however, that the
argument against entities with linkage in header files goes beyond avoiding
ODR violations.

Does it?
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top