3

次のコードが入っていて、警告やエラーが生成さA.cpp ていないのに、inが2回呼び出され、 inが呼び出されない場合。B.cppInitializer::Initializer()B.cppA.cpp

static int x = 0;

struct Initializer
{
    Initializer()
    {
        x = 10;
    }
};

static Initializer init;

これは単一定義規則に違反し、未定義の動作を引き起こしているため、これは完全に正常だと思います。ただし、次のように、コンストラクター定義をいずれかまたは両方のファイルのクラス宣言の外に移動すると、次のようになります。

static int x = 0;

struct Initializer
{
    Initializer();
};

Initializer::Initializer()
{
    x = 10;
}

static Initializer init;

リンカは突然、エラーが発生して言うほど賢くなりますone or more multiply defined symbols found。ここで何が変わり、なぜそれが重要だったのですか?リンカーは常にODRの破損を検出できるはずだと思っていたでしょうが、検出できない場合はどうなりますか?

私が間違っている場合は誰かが私を訂正しますが、私の理論では、テンプレート化されたコード(定義は常にヘッダーにあります)を使用すると、多くのコンパイル単位で定義が重複することになります。それらはすべて同一であるため、リンカーが1つを選択し、他を孤立させるかどうかは問題ではありませんが、複数の定義があるか、テンプレートが機能しないことは間違いありません。

4

3 に答える 3

2

2番目の例には、1つの定義規則の違反を明確かつ簡単に診断できます。これには、外部リンケージを持つ非インライン関数の2つの定義があります。リンクしているオブジェクトファイルに含まれている関数の名前から違反が明らかであるため、リンカはこれを簡単に診断できます。

最初の例では、1つの定義規則を非常に微妙な方法で破っています。クラス本体で定義された関数は暗黙的に宣言されinlineているため、関数本体を調べて、1つの定義規則に違反していることを確認する必要があります。

この理由だけで、あなたの実装が違反を発見できなかったのは驚きではありません(私は初めてではありませんでした)。明らかに、コンパイラが1つのソースファイルを分離して見たときに違反を見つけることは不可能ですが、違反とリンク時間を検出するための情報が、リンカに渡されるオブジェクトファイルを実際に提示していない可能性があります。それは確かに私がリンカーが見つけることを期待する範囲を超えています。

于 2012-06-19T06:02:00.607 に答える
2

複数定義されたシンボルエラーは、複数の変換ユニットが、インライン関数またはメソッドである場合を除いて、外部リンケージを持つアイテムに対してそれぞれ同じ名前署名を持っている場合に発生します。

インライン化された関数は、通常、コンパイル時にODRの対象になりません。もしそうなら、メソッドのインライン実装はどこでも壊れてしまうでしょう。ただし、ODRは、1つのインライン関数が選択されるという点で、リンク時間中に遡及的に適用されます。したがって、同じシグニチャを持つインライン関数は同じように動作することが期待されます。インラインコンストラクターはその期待に違反しています。

代わりに、次のようにヘッダーファイルでテンプレートを宣言した場合:

#ifndef I_HH
#define I_HH
tempalte <typename T>
struct Initializer {
    Initializer () { x = 10; }
};
#endif

これを使用してとの両方に含め、A.cppそれぞれB.cppに静的インスタンスを作成しました。

static int x;
#include "i.hh"
static Initializer<int> init;

コンパイラーは、不適切に形成されたテンプレート(g ++はとにかくそうしました)について文句を言うべきだと思います。これは、ODRの違反を検出するのと同じくらい良いです(コンストラクターは異なるコンテキストで異なる動作をします)。

于 2012-06-19T07:00:36.380 に答える
0

クラス定義内に関数の実装がある場合、関数はインライン化されますが、リンクする必要はありません。したがって、エラーはありません。定義は1つでなければなりませんが、別のcppファイルに含まれていてもかまいません。たとえば、ヘッダーファイルに1つのクラスを定義しましたが、ヘッダーファイルを別のcppファイルにインクルードしました。

于 2012-06-19T06:06:07.063 に答える