Why does this work?
I see similar SO questions stating that it does, but could someone explain it in more detail? Particularly, is this behavior protected by a standard?
i.h
#ifndef I_H_
#define I_H_
typedef void (*FuncPtr)();
template<typename T>
void FuncTemplate() {}
class C {};
#endif
a.cc
#include "i.h"
FuncPtr a() {
return &FuncTemplate<C>;
}
b.cc
#include "i.h"
FuncPtr b() {
return &FuncTemplate<C>;
}
m.cc
#include <iostream>
#include "i.h"
FuncPtr a();
FuncPtr b();
int main() {
std::cout << (a() == b() ? "equal" : "not equal") << std::endl;
return 0;
}
Then
$ g++ -c -o a.o a.cc
$ g++ -c -o b.o b.cc
$ g++ -c -o m.o m.cc
$ g++ a.o b.o m.o -o prog
$ ./prog
equal
Tossing -Wall -Wextra -Werror -ansi onto all the g++ calls produces the same.
My (naive) understanding is that FuncTemplate is instantiated once in each of the a.o and b.o compilation units, and so the addresses should each point to one copy. How do these end up the same after all, and is this behavior portable or protected?
EDIT The shared library case:
$ g++ -shared -o liba.so a.cc
$ g++ -shared -o libb.so b.cc
$ g++ -c -o m.o m.cc
$ g++ -L. -la -lb m.o -o prog
$ ./prog
equal
This is covered under the one definition rule:
3.2 One definition rule [basic.def.odr]
Paragraph 5:
There is a whole list of criteria that follow that have to be-adhered to or its undefined behavior. In the above these do hold. Then …
So technically you can have a copy of the function in each translation unit.
It looks like the wording in the last phrase though makes it a requirement that they all behave the same. This means taking the address of any of these objects should result in the same address.