Can you explain me how ODR does work actually?
Here is an example of violation of ODR:
/* file : module.cpp */
#include <stdio.h>
inline int foo() {
printf("module.foo: %p\n", &foo);
return 1;
}
static int bar = foo();
/* file : main.cpp */
#include <stdio.h>
inline int foo() {
printf("main.foo: %p\n", &foo);
return 2;
}
int main(int argc, char *argv[]) {
return foo();
}
As you can see, function int foo()
defined differently in two modules. Now observe, how it produces different behavior depending on requested optimization level (O3 vs O0):
$ clang++ --std=c++11 -O0 main.cpp module.cpp && ./a.out
module.foo: 0x100a4aef0
module.foo: 0x100a4aef0
$ clang++ --std=c++11 -O3 main.cpp module.cpp && ./a.out
module.foo: 0x101146ee0
main.foo: 0x101146ee0
Output is different, because for inline functions compiler produces a linker symbol in each compilation module. This symbol (in each compilation module) is marked as "pick any, they are all the same". In the first case, when all optimizations are disabled, linker picks up definition from module.cpp. In the second case, compiler just inlines both functions so no additional work from linker is required.
There are other examples when violation of ODR produces weird behavior. So, don't do it :)
P.S. Bonus, case from real life:
/* a.cpp */
struct Rect
{
int x,y,w,h;
Rect(): x(0),y(0),w(0),h(0)
};
/* b.cpp */
struct Rect
{
double x,y,w,h;
Rect(): x(0),y(0),w(0),h(0)
};
The problem here is the same as in previous example (because Rect constructors are implicitly inline). Depending on phase of the moon compiler was picking one implementation or another producing strange results (int
version will leave part of doubles
uninitialized, double
version will go outside of int
s and corrupt memory there). Good way to protect from that is to use anonymous namespaces (C++) or declare struct as static
(C):
/* file.cpp */
namespace {
struct Rect { ... };
}
/* file.c */
static struct Rect { ... };