C++の class template を使えば static メンバの実体がヘッダファイルに書けるカラクリ
C++の class template を使えば static メンバの実体がヘッダファイルに書けるというテクニックがある。考えてみると不思議な動作に思える。だって分割コンパイルしたら実体が複数個出来そうじゃない?。この裏側で起こっている事を実験前に予想して試したところ、予想通りだったのでうれしかったのでメモを残す。
以下実験。
Hoge.h
class template で static メンバの実体をヘッダに書く。
#include <stdio.h> template <typename T> class Hoge { private: static int counter; public: void incrementAndShow() { printf("counter = %d\n", ++counter); } }; template <typename T> int Hoge<T>::counter = 0;
a.cpp
#include "Hoge.h" void funcA() { Hoge<bool> hoge; hoge.incrementAndShow(); }
b.cpp
#include "Hoge.h" void funcB() { Hoge<bool> hoge; hoge.incrementAndShow(); }
main.cpp
extern void funcA(); extern void funcB(); int main(int argc, char *argv[]) { funcA(); funcB(); }
コンパイルと実行
a.cpp, b.cpp, main.cpp は分割してそれぞれコンパイルする。
% g++ -c a.cpp % g++ -c b.cpp % g++ main.cpp a.o b.o
実行すると意図通りの動作になる。でも実体が 2 つ出来てしまうんじゃないの?
% ./a.out counter = 1 counter = 2
-S でアセンブリ見る
.LC0: .string "counter = %d\n" .section .text._ZN4HogeIbE16incrementAndShowEv,"axG",@progbits,_ZN4HogeIbE16incrementAndShowEv,comdat .align 2 .weak _ZN4HogeIbE16incrementAndShowEv .type _ZN4HogeIbE16incrementAndShowEv, @function
予想通り weak シンボルでした。b.s にも同名のシンボルがある。
objdump で見る
objdump -t ./a.out するとリンク時に counter シンボルが1つにまとまって bss に置かれている事が分かる。
0000000000400670 g F .text 0000000000000016 _Z5funcBv 0000000000601038 w O .bss 0000000000000004 _ZN4HogeIbE7counterE # ここ 0000000000601028 g *ABS* 0000000000000000 _edata 0000000000400510 F *UND* 0000000000000000 __gxx_personality_v0@@CXXABI_1.3 0000000000400604 g F .text 0000000000000020 main 00000000004004c8 g F .init 0000000000000000 _init
結論
ヘッダだけで書けるとうれしいね><。