В общем немного покопавшись нарисовалась вполне ожидаемая ситуация:
В получившемся исполняемом файле вызывается одна и та же версия метода A::foo()
objdump -d a.exe
00401600 <__Z4fooBv>:
call 402890 <__ZN1A3fooEv>
0040161c <__Z4fooAv>:
call 402890 <__ZN1A3fooEv>
00401638 <_main>:
401643: e8 d4 ff ff ff call 40161c <__Z4fooAv>
401648: e8 b3 ff ff ff call 401600 <__Z4fooBv>
После компиляции в объектных файлах используется одно и тоже имя для метода A::foo()
objdump -t b.o
b.o: file format pe-i386
SYMBOL TABLE:
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 b.cpp
File
[ 2](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text$_ZN1A3fooEv
AUX scnlen 0x17 nreloc 2 nlnno 0 checksum 0x0 assoc 0 comdat 2
[ 4](sec 5)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x00000000 __ZN1A3fooEv
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 6](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 __Z4fooBv
objdump -t a.o
a.o: file format pe-i386
SYMBOL TABLE:
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 a.cpp
File
[ 2](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text$_ZN1A3fooEv
AUX scnlen 0x17 nreloc 2 nlnno 0 checksum 0x0 assoc 0 comdat 2
[ 4](sec 5)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x00000000 __ZN1A3fooEv
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 6](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 __Z4fooAv
Ну и судя по всему, во время линковки, линковщик находит первую реализацию _ZN1A3fooEv и дважды подставляет ее, ведь и в одном и другом объектном файле используется одно и тоже имя. Непонятно только почему он не стал искать вторую реализацию этой функции во втором объектном файле... Может стоит переместить этот вопрос на SO? Я думаю там могут дать более развернутый ответ.