Re: Duplicate symbols combined without warning
On 7 Mai, 19:43, nils.hje...@gmail.com wrote:
Anyway, if I have two cpp files with identical class declarations (but
with different semantics) of an internal class, and link them
together, one of the implementations get thrown out without any kind
of warning.
You are describing something, for which the standard
has a special name: Your are going to violate the so-called
one-definition rule (ODR), v.i.
Simple example:
foo.cpp
=========================================
#include <iostream>
class DuplicateClass
{
public:
void foo() { std::cout << "foo" << std::endl; }
void foobar() { std::cout << "first foobar" << std::endl; }
};
void foo()
{
DuplicateClass test;
test.foo();
test.foobar();
}
bar.cpp
=========================================
#include <iostream>
class DuplicateClass
{
public:
void bar() { std::cout << "bar" << std::endl; }
void foobar() { std::cout << "second foobar" << std::endl; }
};
void bar()
{
DuplicateClass test;
test.bar();
test.foobar();
}
main.cpp
==========================================
void foo();
void bar();
int main (int argc, char const *argv[])
{
foo();
bar();
return 0;
}
Compiled with
g++ -o test main.cpp foo.cpp bar.cpp -Wall
I get the following output when executing the program
foo
first foobar
bar
first foobar
The test.foobar() call in bar.cpp should output 'second foobar' not
'first foobar'. Looking at the symbols in the executable file it is
obvious that one of the colliding symbols have been thrown out (only
one foobar entry):
$ nm test | grep DuplicateClass
00001c96 T __ZN14DuplicateClass3barEv
00001bce T __ZN14DuplicateClass3fooEv
00001c12 T __ZN14DuplicateClass6foobarEv
Why won't the compiler/linker give me a warning/error when doing this?
Because that compiler can generally not recognize this
kind of user-error. The standard does also not define
any form of diagnostic of a "linker". Because of this,
the standard has clearly described your scenario as
a violation of the ODR. Some kinds of violations are
diagnosable, e.g. [basic.def.odr]/4:
"Exactly one definition of a class is required in a
translation unit if the class is used in a way that
requires the class type to be complete."
But, if the same class definitions are defined in
different translation units, then p. 5 applies:
"There can be more than one definition of a class type
[..] in a program provided that each definition appears in
a different translation unit, and provided the definitions
satisfy the following requirements.
Given such an entity named D defined in more than one translation
unit, then
? each definition of D shall consist of the same sequence of tokens;
and
? in each definition of D, corresponding names, looked up according
to
3.4, shall refer to an entity defined within the definition of D,
or shall refer to the same entity, [..]
If the definitions of D do not satisfy these requirements, then the
behavior is undefined."
The last sentence gives the implementation no restriction what
might happen with your program. So, whether it would use both
definitions or only one is in no way defined, because your
program has violated these rules.
I had a really nasty bug resulting from this, which was pretty
difficult to track down. I tried declaring the classes with 'static
class' thinking it would work as 'static function' does in C, but it
is obviously not valid C++ syntax.
Right, there does not exist a 'static' class in C++.
Is it not possible to limit the scope of the class to the local cpp
file? And why does not the compiler provide a warning when this
happens??
Why do you not use a different namespace for each class?
This is the obvious solution in C++. If both classes are
local for each translation unit, the easiest workaround is
probably to wrap both in an *unnamed* namespace, e.g.:
foo.cpp
=========================================
#include <iostream>
namespace {
class DuplicateClass
{
public:
void foo() { std::cout << "foo" << std::endl; }
void foobar() { std::cout << "first foobar" << std::endl; }
};
}
void foo()
{
DuplicateClass test;
test.foo();
test.foobar();
}
bar.cpp
=========================================
#include <iostream>
namespace {
class DuplicateClass
{
public:
void bar() { std::cout << "bar" << std::endl; }
void foobar() { std::cout << "second foobar" << std::endl; }
};
}
void bar()
{
DuplicateClass test;
test.bar();
test.foobar();
}
This works, because each unnamed namespace behaves as if it
would reference a *unique* namespace per translation unit.
The additional advantage is, that the compiler does the work
for you, because otherwise you would have to ensure that
both namespaces have different names.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]