18

Consider this file, first.cpp, containing a class definition and use:

#include <iostream>

struct Foo
{
    Foo(){ std::cout << "Foo()" << std::endl; }
    ~Foo(){ std::cout << "~Foo()" << std::endl; }
};

int main(){
    Foo f;
    return 0;
}

and another, second.cpp, containing a conflicting class definition:

#include <iostream>

struct Foo
{
    Foo();
    ~Foo();
};

Foo::~Foo(){ std::cout << "wrong ~Foo()" << std::endl; }

The linker complains about duplicate symbols when there are two functions with the same names defined, but these files with duplicate class methods compile without an error.

I compiled with these commands:

$ g++ -c second.cpp -o second
$ g++ second first.cpp -o first

Reordering the arguments to the second g++ call doesn't change the output.

And when first is run, this is the output:

$ ./first
Foo()
wrong ~Foo()

Why does the linker allow duplicate class methods? If it's apparently allowed, why is wrong ~Foo() printed?

Rafał Rawicki
  • 22,324
  • 5
  • 59
  • 79
  • I think it depends on the version of the compiler, but it takes the first one it finds. – Brady May 25 '12 at 12:28
  • 3
    It's probably something to do with function inlining giving way to an object file function where it's present. I'd guess you'd have the same issue with the constructor if you declared a non-inline version in second.cpp and the problem would go away if both sources declared the functions inline. – forsvarir May 25 '12 at 12:42
  • I just tested what @forsvarir mentions and that's correct. In first.cpp I took the Foo destructor definition out of the class (I did it like it is in second.cpp) and I get the duplicate error you are expecting. – Brady May 25 '12 at 12:45
  • I think this sums up why you're not getting multiple definition errors http://stackoverflow.com/a/2501783/592182 and this gives a more verbose background: http://stackoverflow.com/a/603454/592182 – forsvarir May 25 '12 at 13:17

1 Answers1

16

Again, Undefined Behavior. Your program has multiple definitions for the destructor of Foo, which means that it is in violation of the ODR. The program is wrong and anything can happend.

Why does the linker not pick it up? When a function is defined inside the class definition, it is implicitly inline. Compilers usually mark those functions as 'weak symbols'. The linker then gets all translation units and tries to resolve the symbols. Weak symbols will be dropped by the linker if needed (i.e. if the symbol is already defined somewhere else).

As of the actual output of the program, it looks like the compiler did not actually inline the call to the constructor and thus dispatched at runtime to the symbol that was left by the linker (the non-weak one)


Why linker allows to have duplicate methods?

Because all (but at most one) are weak symbols (i.e. inline)

Why, in this case, wrong ~Foo() is printed?

Because the call was not inlined, and the linker dropped the weak symbol

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    For some reason seeing **Undefined Behavior** in bold makes me all giddy. – Captain Obvlious May 25 '12 at 17:03
  • @ChetSimpson: My bad, this question is very very similar to another one from today where the answer was that it is UB. This being almost the same, I mistakenly assumed that the same person was insisting on walking the bleeding edge, but in hindsight it seems that the two questions were asked by different people. – David Rodríguez - dribeas May 25 '12 at 17:05
  • @@david-rodriguez-dribeas It's all good, UB needs to be bold every now and again ;) – Captain Obvlious May 25 '12 at 17:31
  • Thanks! I was aware of ODR and the fact, that this code is not correct, but I expected that linker would report error in this case. – Rafał Rawicki May 25 '12 at 18:33