Reputation: 1316
I came across a curious bug today which I had somehow managed to avoid up until now.
file1.cpp:
#include <iostream>
inline void print() { std::cout << "Print1\n"; }
void y() { print(); }
file2.cpp:
#include <iostream>
inline void print() { std::cout << "Print2\n"; }
void x() { print(); }
main.cpp:
int x();
int y();
int main(){
x();
y();
}
Output:
Print1 (Expected Print2)
Print1
Because print()
has inline linkage, this produces no multiple definition error (compiled with g++ -Wall file1.cpp file2.cpp main.cpp
) and the duplicate symbol is silently collapsed. The actual case where I saw this was with inline class methods, not explicit inline functions, but the effect is the same.
I am wondering if there is a linker option or similar which will allow me to produce a warning when this type of error is made?
Upvotes: 7
Views: 519
Reputation: 96251
The functions are not internal linkage or in an anonymous namespace, so they're visibly external with the same name. They have different bodies so you've clearly violated the one definition rule. At that point any speculation as to what will happen isn't useful as your program is malformed.
I'm guessing that you compiled without optimization and the compiler generated function calls instead of actually inlining, and it picked one of the bodies to use as the function to call (leaving the other one orphaned). Now presumably if you compiled with optimization on, your expected output would be emitted but your program would still be incorrect.
EDIT for comment: Unfortunately it's not required for compilers to diagnose violations of the one definition rule (and they may not even be able to detect all cases). However there are a few things you can do:
static
or in an anonymous namespace (I prefer the namespace for logical grouping purposes but either is fine).inline
methods (regardless of location) as they tell the compiler explicitly that all versions will be identical (for non-inline methods you'll most likely at least get a duplicate symbol error from the linker). The safest approach here is to avoid global-namespace inline functions (or take particular care in your naming). Also you need to be really careful that changed #define
s don't change the function body (for example using assert
in an inline function where one use has NDEBUG
and the other doesn't).Upvotes: 3
Reputation: 134
To make a function local to a .cpp (or .c) file you have to make that function as static
. When you declared print in two separate .cpp files both of them where compiled to print_v
, The second symbol was dropped(ignored)* by the compiler.
This is just an assumption of how it acted on my machine. I tried the same code on a MAC OS X mountain Lion and I got the same result as you, but I was able to solve it by adding static
to both functions.
* if you change the order of the files in the compiling, the behavior would change. try g++ file{2,1}.cpp main.cpp
and the output should be
print2
print2
Sincerely, Abdulrahman Alotaibi
Upvotes: -2
Reputation: 46738
It isn't necessary/mandated that an error/warning be generated, by the standard. It is a violation of C++'s One Definition Rule, because of their having different function-bodies.
Quoting from: Extern Inlines by Default
The meaning of "extern inline" is that if calls to a function are not generated inline, then a compiler should make just one copy of the definition of the function, to be shared across all object files.
If the above occurs, that program's behavior is considered undefined according to the language standard, but that neither the compiler nor the linker is required to give a diagnostic message. In practice, this means that, depending on how the implementation works, the compiler or linker may just silently pick one of the definitions to be used everywhere.
The behaviour is consistent with GCC's definition of vague linkage here.
Upvotes: 2
Reputation: 53506
I am going to shamelessness copy-paste from an email thread I found here on a similar topic...
This is not a gcc issue.
You didn't mention which OS you are using. I will assume it is GNU/Linux. On GNU/Linux, or any other ELF based system, there is a single global namespace of global variables and functions. When two shared libraries use the same global variable name, they are referring to the same variable. This is a feature.
If you want something different to happen, look into symbol visibility and linker version scripts.
It would seem that you should be able to tell the linker/compiler that symbol X should be unique and complain if it isn't though.
Upvotes: 0