2017-09-21 13 views
1

は、私は2つの異なる翻訳単位a.cpp競合テンプレート定義とODR

#include <iostream> 

double bar(); 

template <typename T> 
T foobar(T t) { 
    return t; 
} 

int main() { 
    std::cout << "foobar called from b.cpp: " << bar() << '\n'; 
    std::cout << "foobar called from a.cpp: " << foobar(1.) << '\n'; 
} 

とb.cppを持っている状況を想像してみてODR、すなわちコンパイラは、インスタンス化された関数テンプレートをそのようにマークし、リンク処理中に1つを除いてすべてを打ち消す。コンパイラは、異なる翻訳単位でのそのようなインスタンス化の生成コードが実際に同一であるか少なくとも同等であるかどうか実際は気にしないことに気付きました。

上記のコードでは、これが該当します。

c++ a.cpp b.cpp -o result -std=c++17 && ./result 

とリンクし、実行している、コンパイルするときには、

foobar called from b.cpp: 1 
foobar called from a.cpp: 1 

だから明らかに、オブジェクトファイルb.o内のインスタンス化はa.o.からそのいずれかの賛成で捨ててしまった結果が得られます

c++ b.cpp a.cpp -o result -std=c++17 && ./result 

のように、コンパイルおよびスワップb.cppとa.cppとリンクする際、結果は

foobar called from b.cpp: 2 
foobar called from a.cpp: 2 

になりますので、全く逆のことが起こりますのリストの最初に言及してしまったのインスタンスをリンクされたオブジェクトファイルは、存続するオブジェクトファイルになります。そのような振る舞いは、標準のどこかで定義されていますか?ビルドシステムによっては、オブジェクトファイルの記述順はむしろ恣意的になる可能性がありますが、そのような例のように非常に異なるプログラムや厄介なバグが発生する可能性があります。私は明示的に

template double foobar<double>(double); 

を追加することにより、a.cppからバージョンをインスタンス化しようとしても、それは、リンカリストの中a.o前b.o言及する時に生き残るa.cppからfoobarに<>テンプレートをすることはありません。

+0

これは単なる学問的な質問ですか、これが問題であるという事実は本当ですか?私はテンプレートを一度しか定義できず、問題がなくなったということです。 – user463035818

+2

[tag:language-lawyer]というタグをあなたの質問に追加したいようですね! –

+0

私の質問に答えて@WernerHenze;) – user463035818

答えて

3

私はテンプレートのために、テンプレートのODRには例外はありませんODR

に例外があることを知っている、それだけでそのテンプレート機能はinlineをされています。

また、プログラムにODR違反があります。
通常のインライン関数と同様の問題があります。

+0

私はそれを再現することができます。私はあなたが明らかにODRに違反し、リンカがエラーや警告を出すことなく、基本的にインライン関数やテンプレート関数の定義を追加するだけでUBに簡単に実行できることに気づいていませんでした。 – Jodocus

関連する問題