views:

92

answers:

6

Let's just I had the following code:

foo.h

class Foo
{
    // ...
};

foo.cpp

#include "foo.h"
// Functions for class Foo defined here...

Let's say that Foo are built into a static library foo.lib.

Now let's say I have the following:

foo2.h

class Foo
{
    // ...
};

foo2.cpp

#include "foo2.h"
// Functions for class Foo defined here...

This is built into a separate static library foo2.lib.

Now, if I re-link foo.lib and foo2.lib into an executable program foo.exe, should it be complaining that class Foo has been defined twice?

In my experiences, neither the compiler or the linker are complaining.

I wouldn't be expecting the compiler to complain, because they have been defined in separate translation units.

But why doesn't the linker complain?

How does the linker differentiate between the 2 versions of the Foo class? Does it work by decorating the symbols?

A: 

You have to include one of those foo headers. The compiler will only know about the included Foo class and perhaps due to name mangling the linker might not complain too.

On the other hand if you include both headers the compiler should complain and the linker might complain anyway.

LumpN
But as long as I never include both headers I should be okay? That's what I'm seeing, but I'm trying to figure out why the linker doesn't complain.
LeopardSkinPillBoxHat
@Leopard: are you perhaps using different namespaces for these two otherwise identical classes ?
Paul R
@Paul R - I haven't explicitly defined any namespaces, so would this mean they are using the same namespace?
LeopardSkinPillBoxHat
A: 

I believe the compiler should complain before the linker does. Let me explain better.

  • The compiler handles perfectly the compilation of the two separate units, as expected.
  • When the compiler gets a new file referencing both "foo.h" and "foo2.h" (as per Andreas example) there should be an immediate naming conflict.

If the two libraries are referenced separately (no source/header file and dependencies reference both) it might be the case that the program compiles and links correctly due to name mangling, but I am not sure about it.

@Igor (cannot comment... duh!) In the page you ling about the ODR it says the exact contrary of what you stated (see the first of the Examples). You can have multiple entities with the same name and multiple definitions only according to special rules. The number of declarations is also dependent on the kind of entity.

NeXuS
The asker is specifically NOT asking about the case where both headers are included in a single translation unit.
pkh
Uhm.. and where do you read it? I only see "Now, if I re-link foo.lib and foo2.lib into an executable program foo.exe, should it be complaining that class Foo has been defined twice?"
NeXuS
+2  A: 

No, neither compiler nor linker required to complain. They might; but it's not required.
See the One Definition Rule.

Igor Krivokon
From the link: "In the entire program, an object or non-inline function cannot have more than one definition; if an object or function is used, it must have exactly one definition. You can declare an object or function that is never used, in which case you don't have to provide a definition. In no event can there be more than one definition."
Andreas Brinck
The ODR doesn't appear to apply to classes in different translation units (types are neither objects nor functions), so there's no violation of that rule here. I'd expect duplicate symbols for any duplicated method signatures, but no other error from the linker. Now if there are virtual methods the situation may be different.
ergosys
@ergosys - There are virtual destructors in each class. In my case I'm not getting a compiler or linker error.
LeopardSkinPillBoxHat
+2  A: 

To cause this to generate a linker error, you'll need to force a symbol with external linkage to occur that's shared between the two versions of foo -- and both those symbols must be used. Since the only symbols from a class that can have external linkage are member functions, you're going to have to have a non-inlined function in each Foo that has the same name.

The practical upshot is: this may work, and it may not, and you're not going to know for any given case until you try (since you can't force the compiler to inline functions). Don't do it.

For example, in the following code you'll get a multiply-defined Foo::Foo at link-time:

foo.h

class Foo
{
public:
    Foo();
    int a;
};

foo2.h

class Foo
{
public:
   Foo();
   int b;
};

foo.cpp

#include "foo.h"
#include <cstdio>

Foo::Foo() : a(22) {}

void do_foo()
{
    Foo foo;
    std::printf("%d\n", foo.a);
}

foo2.cpp

#include "foo2.h"
#include <cstdio>

Foo::Foo() : b(33) {}

void do_foo_2()
{
    Foo foo;
    printf("%d\n", foo.b);
}

main.cpp

extern void do_foo();
extern void do_foo_2();

int main(int argc, char** argv)
{
    do_foo();
    do_foo_2();
}
pkh
@pkh - you are correct if you directly link foo.o and foo2.o into your application. But the OP added in the extra complication that foo.o is packaged into one static library and foo2.o is packaged into a second static library. Due to the special treatment static libraries get, this example will not generate a linker error when packaged into static libraries. That said, I'll still give you a +1 since you hit the important point of symbols with external linkage.
R Samuel Klatchko
+1  A: 

Part of the issue is that each class is in a static library. Linkers treat static libraries specially and only extract the necessary objects to satisfy missing symbols that have linkage.

Whether you get a linking error or not will depend on what symbols each implementation of foo has and how they are packaged up together. Since you have simple packaging (a single .cpp file for each foo that holds all the methods), it's easy to generate a conflict.

So, let's create a situation where you will get a conflict. You want to define one foo like this:

class foo
{
public:
    foo();
    int a() const;
};

and your second foo like this:

class foo
{
public:
    foo();
    int b() const;
};

Now each of those foo's have one identical symbol with linkage for the default constructor and a second different symbol.

Now in your application, have one function in one file that does:

int lib1()
{
    foo f;
    return f.a();
}

and a second file that does this:

int lib2()
{
    foo f;
    return f.b();
}

Now your application will have three symbols that it needs, foo::foo, foo::a and foo::b. Because of foo::a and foo::b, the linker will try to use both static libraries but will then cause two different copies of foo::foo to get pulled in which will cause the linker to have an error.

R Samuel Klatchko
+2  A: 

You can have more than one definition of a class type in multiple translation units subject to some fairly strong restrictions meaning that the definitions must be virtually identical. (3.2 [basic.def.odr])

This also applies to enumeration types, inline functions with external linkage, class template, non-static function template, static data member of a class template, member function of a class template or a template specialization for which some template parameters are not specified.

What this effectively means is that you can follow the common practice of putting a class definition in a header file and using this in multiple translation units so long as there aren't any differences in the include sequence that would cause the token sequence or meaning of any names used in the class definition to differ between the multiple translation units.

What you can't have is more than one definition of a non-inline member function of a class across the whole program.

Violations of either of these rules causes undefined behaviour so no part of the compile sequence is required to generate a diagnostic or cause any particular behaviour so if you have two definitions of a class that are slightly different things my appear to work or may cause strange issues at runtime.

It is very likely that if you have two definitions of a non-inline member function of an identically named class with and identical signature that you will have errors at link time but this isn't a language guarantee.

It's worth noting that if two definitions for the same entities are in separate object files in libraries (either the same or difference libraries) it is likely that your program will not actually include more than one definition for a given entity. The way linkers traditionally work is that the iteratively select object files that help resolve symbols in the program but they leave out object files that don't help resolve symbols. This means that after the first object file with a definition is included there is no need to include the second object file with an alternative definition.

Charles Bailey