質問: c++ で template関数を使う場合に特殊化により関数を制作する時に、template関数が定義されているヘッダーを複数のソースに使う場合に同じ関数が二度生成されてしまいますが、これはどう解決すればよいですか? 例えば次のような場合です。 ``` // header_temp.hpp template void foo(T a) { cout << a << endl; } // source_1.cpp #include "header_temp.hpp" #include "header_class_alice.hpp" // class Aliceが定義されている void hoge() { foo(10); } // source_2.cpp #include "header_temp.hpp" #include "header_class_alice.hpp" void fuga() { foo(10); } ``` 上記の場合はsource_1.cppのコンパイルの際に関数`foo`が生成され、source_2.cppをコンパイルする際にまた関数`foo`が生成されます。今、関数`foo`はinlineに定義されていないので、厳密には同じ関数が二度定義されています。これはリンクの際に問題を起こします。この解決のためにはソースの内一つで`template void foo(Alice)`して、残りのソースでは`extern void foo(Alice);`するか、template fooの定義をそもそもinlineにすることですが、関数が大きくなるとinlineにしてソースごとに関数の定義をしてしまうのはバイナリーサイズの観点で浪費があると考えます(完全に同じ関数を二回定義しているので)。 template関数自体は共有ライブラリーみたいにはできないので(その特殊化はできますが)、このような問題は必ず生じかねないとは思いますが、もし上記のtemplate, externを使った方法以外にプログラムバイナリーでtemplateにより生成された特殊化関数`void foo(Alice)`が一つだけ存在する様にするにはどうすればよいでしょうか? そしてこのことによると、stlのvectorなどのcontainerは複数のソースで`vector`で定義されたcontainerが生成された場合に、ソースごとに`vector::push_back`などのメソッドを生成することになりますが、そうでしょうか?
回答: `template` をコンパイルすると(特殊化しても・しなくても)、複数個の翻訳単位で同じ関数・クラス定義が翻訳され、 複数個のオブジェクトファイル中に同じ変数や関数が生成されます。 現代的 [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") に対応したコンパイラ+リンカなら `template` の翻訳結果は weak symbol/function となります。 weak とはリンク時最適化により、 - 複数のオブジェクトファイル中に複数の実装がある場合に - リンク後の実行形式にはそのうちの1つだけが残るというものです。 異なる翻訳単位にて生成されるであろう「同一変数・関数」が本当に同一内容である場合に限り そのうちの1つだけを残す最適化が適用可能なわけです。 それゆえに One Definition Rule がある、ということになります。 なので質問にあるところの > > ソースごとにvector::push_backなどのメソッドを生成することになりますが、そうでしょうか? > > > この疑問に対する答えは Yes 複数のオブジェクトファイル中に同じ変数・関数が生成されます。 > > 一つだけ存在する様にするにはどうすればよいでしょうか? > > > この疑問に対する答えは「考慮する必要はありません」(処理系側で対処してくれています) > > バイナリーサイズの観点で浪費があると考えます > > > オブジェクトファイルのバイナリーサイズの観点ではまさに無駄そのものです。 最終的に生成される実行形式ファイルのバイナリーサイズは無駄になりません。 非 `template` な、つまり昔ながらの通常関数・変数をコンパイルすると STRONG になり リンク時最適化の挙動は変化します。 STRONG なシンボルは - 重複定義はリンク時エラーとなります - 同一シンボルが STRONG/WEAK の両方で定義されていると STRONG が採用され WEAK は無視されます 同一名称の関数が `template` なものと 非 `template` なものと、両方あるとき `template` なものが消失し 非 `template` なものだけ残るのはこの辺に由来します。