7

今日、これまでどうにか回避できていた奇妙なバグに遭遇しました。

file1.cpp:

#include <iostream>
inline void print() { std::cout << "Print1\n"; }
void y() { print(); }

file2.cpp:

#include <iostream>
inline void print() { std::cout << "Print2\n"; }
void x() { print(); }

main.cpp:

int x();
int y();

int main(){
    x();
    y();
}

出力:

Print1                         (Expected Print2)
Print1

print()インライン リンケージがあるため、複数定義エラー ( でコンパイルg++ -Wall file1.cpp file2.cpp main.cpp) は生成されず、重複したシンボルは黙って折りたたまれます。私がこれを見た実際のケースは、明示的なインライン関数ではなく、インライン クラス メソッドを使用していましたが、効果は同じです。

このタイプのエラーが発生したときに警告を生成できるリンカーオプションまたは類似のものがあるかどうか疑問に思っていますか?

4

4 に答える 4

2

関数は内部リンケージまたは匿名の名前空間にないため、同じ名前で目に見えて外部です。それらは異なるボディを持っているため、1 つの定義ルールに明らかに違反しています。その時点で、何が起こるかについての憶測は、プログラムの形式が正しくないため役に立ちません。

最適化せずにコンパイルし、実際にインライン化する代わりにコンパイラーが関数呼び出しを生成し、呼び出す関数として使用する本体の 1 つを選択したと推測しています (他の 1 つを孤立させたままにします)。おそらく、最適化をオンにしてコンパイルした場合、期待される出力が出力されますが、プログラムは依然として正しくありません。

コメントの編集: 残念ながら、コンパイラが 1 つの定義規則の違反を診断する必要はありません (すべてのケースを検出できるとは限りません)。ただし、できることがいくつかあります。

  • source-private 関数を常に、staticまたは匿名の名前空間で作成します (論理グループ化の目的で名前空間を好みますが、どちらでもかまいません)。
  • すべてのバージョンが同一であることをコンパイラに明示的に伝えるメソッド (場所に関係なく) には特に注意しinlineてください (非インライン メソッドの場合、少なくともリンカーから重複シンボル エラーが発生する可能性が高くなります)。ここで最も安全な方法は、グローバル名前空間のインライン関数を避けることです (または、命名には特に注意してください)。また、変更された s が関数本体を変更しないように注意する必要があります#define(たとえばassert、一方が使用され、もう一方が使用されないインライン関数で使用する場合NDEBUG)。
  • クラスと名前空間を論理的に使用してコードを分割し、同じシンボルの複数の定義を防止します。
于 2013-02-22T20:16:12.530 に答える
0

同様のトピックでここで見つけたメールスレッドから恥知らずにコピーアンドペーストします...

これは gcc の問題ではありません。

使用しているOSについては言及していません。GNU/Linux だと仮定します。GNU/Linux またはその他の ELF ベースのシステムでは、グローバル変数と関数の単一のグローバル名前空間があります。2 つの共有ライブラリが同じグローバル変数名を使用している場合、それらは同じ変数を参照しています。これが特徴です。

何か違うことをしたい場合は、シンボルの可視性とリンカーのバージョン スクリプトを調べてください。

リンカー/コンパイラーに、シンボル X が一意であることを伝え、そうでない場合は文句を言うことができるはずです。

于 2013-02-22T20:13:42.130 に答える
-2

関数を .cpp (または .c) ファイルに対してローカルにするには、その関数を として作成する必要がありますstatic。2 つの別々の .cpp ファイルで print を宣言すると、両方とも にコンパイルされprint_v、2 番目のシンボルがコンパイラによって削除 (無視) されました。

これは、私のマシンでの動作の単なる仮定です。MAC OS X マウンテン ライオンで同じコードを試したところ、あなたと同じ結果が得られましたが、static両方の機能を追加することで解決できました。

※コンパイル時にファイルの順番を入れ替えると挙動が変わってしまいます。試してみるg++ file{2,1}.cpp main.cppと、出力は

print2

print2

アブドゥルラフマン・アロタイビ

于 2013-02-22T23:55:30.207 に答える