8

私は奇妙な問題に何日も費やしましたが、最終的inlineに、プロジェクトに同じ署名の 2 つの関数があり、それらが問題の原因であることを発見しました。状況を簡単にするために、以下に例を示します: 2 つの cpp ファイル:

a.cpp

#include <iostream>

void b();

inline void echo()
{
    std::cout << 0 << std::endl;
}

int main()
{
    echo();
    b();
    return 0;
}

および b.cpp

#include <iostream>

inline void echo()
{
    std::cout << 1 << std::endl;
}

void b()
{
    echo();
}

inline関数echoのシグネチャは同じですが、実装が異なることに注意してください。コンパイルして実行

g++ a.cpp b.cpp -o a.out && ./a.out

または、このように

g++ a.cpp -c
g++ b.cpp -c
g++ a.o b.o -o a.out
./a.out

印刷し0 0ます。(私はそのためにg ++ 4.6.1を使用していました.clang ++ 2.9でテストしましたが、同じ結果です)

次のように最適化をオンにすると、それは起こりません

g++ -O3 a.cpp b.cpp -o a.out && ./a.out

0 1今回です。

私の質問は、結果やコンパイルの実行方法に関係なく、inline関数を複数回定義したというエラーや警告さえないということです。このような状況では、コンパイラとリンカは一体どうなるのでしょうか?

編集:

オブジェクトファイルのシンボルを見てください

nm a.o b.o | c++filt

両方のファイルにレコードがありecho()ます。したがって、問題はリンク時に発生すると思います。リンカーはランダムに 1 つの実装を選択し、他のすべてを破棄すると言えますか?

4

3 に答える 3

13

C++ 標準では、インライン関数のすべての定義が同じであると述べられていますが、診断は必要ありません。つまり、プログラムは有効な C++ プログラムではありませんが、実装にはそのエラーを検出しない権利があります。

条項 3.2.5 を参照してください。ここに投稿するには長すぎます。

于 2011-07-27T11:00:40.323 に答える
6

まさにこのケース (実装が異なる同じ名前と同じ署名を持つ 2 つのインライン関数)は、未定義の動作につながります。コンパイラは診断を試みることはできますが、診断する必要はありません。

于 2011-07-27T11:01:55.340 に答える
5

コンパイラーは、この ODR 違反を診断する必要はなく、簡単ではありません。このinlineキーワードは、異なる翻訳単位が同じ記号を持つ可能性があることを意味するため、コンパイラによって弱いとマークされます。基本的な使用例は、ヘッダーでインラインで定義された関数です。ヘッダーを含むすべての翻訳単位には定義があり、まったく問題ありません。コンパイラは、1 つの定義を除いてすべて破棄し、その定義をどこでも使用するだけで済みます。

異なる定義が完全に一致しているかどうかを検出することは、複雑な問題です。リンカーは、生成されたバイナリ実装を分析し、2 つのバイナリ コードが同じソース コードに関連しているかどうかを判断する必要があります。ほとんどのコンパイラには、これを判断するためのサポートがありません。

あなたの特定の問題の時点​​で、2つの関数がインラインでマークされた理由をおそらく知ることはできませんが、一般的なエラーは、inlineキーワードを使用して最適化を表すのではなく、リンク時に繰り返しについて文句を言わないことです。キーワードはinlineヘッダーでは意味がありますが、cpp ファイルではあまり意味がありません。cpp ファイルでは、コードの一部をヘルパー関数に分解する場合、その関数をマークするか、名前のない名前空間static内で定義する必要があります。関数がstatic次に、コンパイラは、その関数のすべての使用法が翻訳単位内にあることを認識し、関数呼び出しをインライン化するかどうかを決定するためのより多くの知識を持っています (インライン化するように指示しなくてもインライン化できることに注意してください)。 、あなたがそれを伝えてもインライン化しないことを決定できるのと同じ方法で)。

于 2011-07-27T11:29:49.817 に答える