tags:

views:

197

answers:

3

I have the following source files:

//test1.cpp
#include <iostream>
using namespace std;

inline void foo()
{
  cout << "test1's foo" << endl;
}

void bar();

int main(int argc, char *argv[])
{
  foo();
  bar();
}

and

//test2.cpp
#include <iostream>

using namespace std;

inline void foo()
{
    cout << "test2's foo" << endl;
}

void bar()
{
    foo();
}

The output:

test1's foo
test1's foo

Huh??? Ok, so I should have declared the foos static... but shouldn't this kind of thing generate a linker error, or at least a warning? And how does the compiler "see" the inline functions from across compilation units?

EDIT: This is using gcc 4.4.1.

+8  A: 

You are running into the one-definition-rule. You are not seeing any error because:

[Some] violations, particularly those that span translation units, are not required to be diagnosed

What going on under the covers is that the compiler is not inlining those functions (many compilers will not inline a function unless the code is compiled with the optimizer). Since the function is inline and can appear in multiple translation units, the compiler will mark the function as link-once which tells the linker that it not treat multiple definitions as an error but just use one of them.

If you really want them to be different, you want a static function.

R Samuel Klatchko
A static function will work, however an anonymous namespace is the preferred way to accomplish this.
villintehaspam
+5  A: 

R Samuel Klatchko's answer is correct, but I'll answer part that he didn't.

"And how does the compiler "see" the inline functions from across compilation units?"

It doesn't. It sees the external definitions of functions that are declared not to be static. For such functions, the compiler can inline if it wishes but it also has to generate code callable from outside.

Windows programmer
Nice elucidation!
Drew Hall
I agree, Samuel's answer is ostensibly correct, but he should add this information to it. This answer doesn't contain enough information to be a full answer or I would vote for it instead.
Omnifarious
+1  A: 

The inline function is placed in a COMDAT section. That's a signal to the linker that it is free to pick any one of the multiple definitions it encounters. You'll get another output message when you reverse the link order.

Another way to place definitions in a COMDAT section (compiler allowing) is:

__declspec(selectany) int globalVariableInHeader = 42;

Which is handy to avoid the "extern" song and dance. Clearly, this mechanism was designed to allow multiple definitions introduced by one header file getting #included by multiple source files to be resolved by the linker. Fwiw, MSVC has the exact same behavior.

Hans Passant