9

最近ヘッダーファイルでこのコードを見ましたが、それが機能することに驚きました:

namespace NS {
  static int uid = 0;
  class X {
   public:
    static int getUID() { return uid++; }
  };
}

静的メソッドNS::X::getUID()が複数の異なる C++ ソース ファイルから呼び出された場合、一意の ID (翻訳単位全体で一意) が正しく生成されたことに驚きました。名前空間スコープの静的変数には、翻訳単位への内部リンケージがあると思いました。何が起きてる?クラス X のインライン静的メソッドには独自の翻訳単位があり、それが一意の ID を生成する理由ですか? または、コンパイラの癖が原因で機能していますか?

上記のコードは、「安全な」明確に定義された動作に依存していますか? もしそうなら、これはインラインまたはテンプレート クラスで一意の ID を生成する驚くほど簡潔な方法です。それとも、このような静的一意 ID 関数の新しい C++ ソース ファイルを生成し、静的 ID をクラス内に移動する方がよいでしょうか?

アップデート:

テスト ケースとして、次のようないくつかの関数を異なるファイル (file1.cpp、file2.cpp など) に記述しました。

#include "static_def.h" // Name of the above header file.
void func1() {
  int uid1 = NS::X::getUID();
  int uid2 = NS::X::getUID();
  std::cout << "File1, UID1: " << uid1 << ", UID2: " << uid2 << std::endl;
}

驚くべき出力 (これらを main から呼び出した後) は次のとおりです。

File1, UID1: 0, UID2: 1
File2, UID1: 2, UID2: 3
4

2 に答える 2

14

このヘッダーを 1 つのソース ファイルにのみ含める場合にのみ、コードは正しくなります。

uidとして宣言されているためstatic、内部リンケージがあります。uidこのヘッダーが含まれるソース ファイルごとに変数のインスタンスが 1 つあります。このヘッダーを 3 つのソース ファイルに含めると、uid変数は 3 つになります。

getUid関数はinlineクラス定義内で定義されているため、暗黙的です。staticそれがメンバー関数であるという事実は無関係です。inline関数のルールは、inline関数が使用されるすべてのソース ファイルで関数を定義する必要があり、すべての定義が同一でなければならないということです。

あなたのgetUid関数定義はこのルールに違反しています: はい、ヘッダーを含むすべてのソースファイルで定義されています (ヘッダーで定義されているため) が、各定義では参照先が異なる変数であるため、各定義は異なります。uiduid

したがって、プログラムは 1 つの定義規則に違反し、未定義の動作を示します。あなたが観察した特定の動作は、コンパイラがインライン関数の 1 つの定義を選択し、残りを単に破棄するためである可能性がありますuidgetUidコンパイラが保持したコピー。これは未定義の動作のこの形式の典型的な症状ですが、それでも動作は未定義です。

uid変数を関数ローカルにして、インスタンスが 1 つだけ存在するようにし、1 つの定義規則に違反しないようにすることができます。

namespace NS {
  class X {
  public:
    static int getUID() {
      static int uid = 0;
      return uid++;
    }
  };
}

この場合、プログラム内に のインスタンスが 1 つだけ存在することが保証されますuid

于 2013-03-28T15:36:27.577 に答える
2

メソッドをインライン化しないことで、コンパイラがあなたを騙していると思いますgetUID()。暗黙的にインライン化されているという事実getUID()は、それが実際にインライン化されることを意味するものではありません — これはコンパイラへの単なる推奨事項です。

それ以外は、問題ありません。UID には内部リンケージがあり、getUID()一意でない ID を返すこともできます。

于 2013-03-28T15:54:27.720 に答える