名前のない名前空間を使用して、関数と変数に内部リンケージを持たせることを理解しています。名前のない名前空間はヘッダー ファイルでは使用されません。ソースファイルのみ。ソースファイルで宣言された型は、外部では使用できません。では、名前のない名前空間に型を配置することの用途は何ですか?
名前のない名前空間に型を配置できることが言及されているこれらのリンクを参照してください。
名前のない名前空間を使用して、関数と変数に内部リンケージを持たせることを理解しています。名前のない名前空間はヘッダー ファイルでは使用されません。ソースファイルのみ。ソースファイルで宣言された型は、外部では使用できません。では、名前のない名前空間に型を配置することの用途は何ですか?
名前のない名前空間に型を配置できることが言及されているこれらのリンクを参照してください。
名前のない名前空間以外のローカル型をどこに配置しますか? 型に のようなリンケージ指定子を含めることはできませんstatic
。ヘッダーで宣言されているなどの理由でそれらが公に知られていない場合、ローカル型の名前が競合する可能性がかなりあります。たとえば、2 つの翻訳単位が同じ名前の型を定義している場合などです。その場合、ODR 違反になってしまいます。名前のない名前空間内で型を定義すると、この可能性がなくなります。
もう少し具体的に。あなたが持っていると考えてください
// file demo.h
int foo();
double bar();
// file foo.cpp
struct helper { int i; };
int foo() { helper h{}; return h.i; }
// file bar.cpp
struct helper { double d; }
double bar() { helper h{}; return h.d; }
// file main.cpp
#include "demo.h"
int main() {
return foo() + bar();
}
helper
これら 3 つの翻訳単位をリンクすると、 fromfoo.cpp
との定義が一致しなくなりbar.cpp
ます。コンパイラ/リンカーはこれらを検出する必要はありませんが、プログラムで使用される各型には一貫した定義が必要です。この制約に違反することは、「1 つの定義ルール」(ODR) の違反として知られています。ODR 規則に違反すると、未定義の動作が発生します。
コメントを考えると、もう少し説得力が必要なようです。標準の関連セクションは、3.2 [basic.def.odr] パラグラフ 6 です。
クラス型 (条項 9)、列挙型 (7.2)、外部リンケージを持つインライン関数 (7.1.2)、クラス テンプレート (条項 14)、非静的関数テンプレート (14.5.6) の複数の定義が存在する可能性があります。 、クラス テンプレートの静的データ メンバー (14.5.1.3)、クラス テンプレートのメンバー関数 (14.5.1.1)、または一部のテンプレート パラメータが指定されていないテンプレートの特殊化 (14.7、14.5.5)定義が別の翻訳単位に表示され、定義が次の要件を満たしていることが条件です。D という名前のエンティティが複数の翻訳単位で定義されている場合、D の各定義は同じ一連のトークンで構成されます。と [...]
さらに多くの制約がありますが、「トークンの同じシーケンスで構成されなければならない」は、たとえば上記のデモの定義が合法であることを除外するには明らかに十分です。
では、名前のない名前空間に型を配置することの用途は何ですか?
名前の競合の問題なしに、複数のファイルで使用される可能性のある名前を持つ短くて意味のあるクラスを作成できます。
たとえば、名前のない名前空間でInitializer
との 2 つのクラスをよく使用しますHelper
。
namespace
{
struct Initializer
{
Initializer()
{
// Take care of things that need to be initialized at static
// initialization time.
}
};
struct Helper
{
// Provide functions that are useful for the implementation
// but not exposed to the users of the main interface.
};
// Take care of things that need to be initialized at static
// initialization time.
Initializer initializer;
}
このコード パターンを必要な数のファイルで繰り返すことができます。名前が邪魔になることはInitializer
ありません。Helper
OPのコメントに応じて更新
ファイル-1.cpp:
struct Initializer
{
Initializer();
};
Initializer::Initializer()
{
}
int main()
{
Initializer init;
}
ファイル-2.cpp:
struct Initializer
{
Initializer();
};
Initializer::Initializer()
{
}
ビルドするコマンド:
g++ file-1.cpp file-2.cpp
の複数の定義に関するリンカ エラー メッセージが表示されますInitializer::Initializer()
。標準では、リンカーがこのエラーを生成する必要がないことに注意してください。セクション 3.2/4 から:
すべてのプログラムには、そのプログラムで ODR で使用されるすべての非インライン関数または変数の定義が 1 つだけ含まれている必要があります。診断は必要ありません。
関数がインラインで定義されている場合、リンカーはエラーを生成しません。
struct Initializer
{
Initializer() {}
};
実装は同一であるため、このような単純なケースでは問題ありません。インライン実装が異なる場合、プログラムは未定義の動作をする可能性があります。