質問
C ++の機能は、次のような名前のない(匿名の)名前空間を作成する機能です。
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
このような機能は役に立たないと思います。名前空間の名前を指定できないため、外部からその中にあるものにアクセスすることは不可能です。ただし、これらの名前のない名前空間は、作成されたファイル内でアクセスできます。暗黙のusing句が使用されているかのように。
これは、静的関数を使用するよりも、なぜ、またはいつ望ましいかという質問です。または、本質的にまったく同じことを行う2つの方法ですか?
解決
C ++標準では、セクション7.3.1.1の名前のない名前空間、段落2を読み取ります。
静的キーワードの使用は でオブジェクトを宣言するときに非推奨 名前空間スコープ、名前のない名前空間 優れた代替手段を提供します。
Staticは、オブジェクト、関数、および匿名共用体の名前にのみ適用され、型宣言には適用されません。
編集:
この静的キーワードの使用を非推奨にする決定(翻訳単位の変数宣言の可視性に影響)は逆になりました( ref )。この場合、静的な名前空間または名前のない名前空間を使用することは、本質的にまったく同じことを行う2つの方法に戻ります。詳細については、これ SOの質問をご覧ください。
名前のない名前空間には、翻訳単位ローカル型を定義できるという利点があります。詳細については、こちら SOの質問をご覧ください。
クレジットは、これを私の注意に導いたことでマイクパーシーに送られます。
他のヒント
匿名の名前空間にメソッドを置くことにより、 1つの定義ルールに誤って違反することを防ぎます。ヘルパーメソッドの名前を、リンクする可能性のある他のメソッドと同じ名前にすることを心配しないでください。
そして、lukeが指摘したように、匿名の名前空間は静的メンバーよりも標準によって優先されます。
静的には驚くべき効果があります(少なくとも私にはそうでした)。 C ++ 03標準では、14.6.4.2 / 1:に記載されています。
テンプレートパラメータに依存する関数呼び出しの場合、関数名が unqualified-id であるが template-id ではない場合、候補関数は以下を除く通常のルックアップルール(3.4.1、3.4.2):
- 非修飾名ルックアップ(3.4.1)を使用したルックアップの部分では、テンプレート定義コンテキストからの外部リンケージを持つ関数宣言のみが見つかります。
- 関連付けられた名前空間(3.4.2)を使用したルックアップの部分では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった外部リンケージを持つ関数宣言のみが見つかります。
...
以下のコードは、 foo(S const&)
ではなく、 foo(void *)
を呼び出します。
template <typename T>
int b1 (T const & t)
{
foo(t);
}
namespace NS
{
namespace
{
struct S
{
public:
operator void * () const;
};
void foo (void*);
static void foo (S const &); // Not considered 14.6.4.2(b1)
}
}
void b2()
{
NS::S s;
b1 (s);
}
それ自体はおそらくそれほど大したことではないかもしれませんが、完全に準拠したC ++コンパイラ(つまり、 export
をサポートしているもの)では、 static
キーワード他の方法では利用できない機能が引き続きあります。
// bar.h
export template <typename T>
int b1 (T const & t);
// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
foo(t);
}
// foo.cc
#include "bar.h"
namespace NS
{
namespace
{
struct S
{
};
void foo (S const & s); // Will be found by different TU 'bar.cc'
}
}
void b2()
{
NS::S s;
b1 (s);
}
名前のない名前空間の関数がADLを使用するテンプレートで見つからないようにする唯一の方法は、 static
にすることです。
Modern C ++の更新
C ++ '11の時点では、名前のない名前空間のメンバーには暗黙的に内部リンクがあります(3.5 / 4):
名前のない名前空間、または名前のない名前空間内で直接または間接的に宣言された名前空間には、内部リンケージがあります。
同時に、14.6.4.2 / 1が更新され、リンケージの記述が削除されました(これはC ++ '14から取られました):
postfix-expressionが従属名である関数呼び出しの場合、候補関数は次を使用して検出されます 以下を除く通常のルックアップルール(3.4.1、3.4.2):
非修飾名検索(3.4.1)を使用した検索の部分では、テンプレート定義コンテキストからの関数宣言のみが見つかります。
関連する名前空間(3.4.2)を使用したルックアップの部分では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった関数宣言のみが見つかります。
結果は、静的な名前空間メンバーと名前のない名前空間メンバーのこの特定の違いはもはや存在しないということです。
最近、コード内で静的キーワードを匿名の名前空間に置き換え始めましたが、すぐにデバッガで名前空間の変数を検査できないという問題に遭遇しました。 VC60を使用していたので、それが他のデバッガーの問題ではないかどうかはわかりません。私の回避策は、「モジュール」名前空間を定義し、cppファイルの名前を付けることでした。
たとえば、私のXmlUtil.cppファイルでは、すべてのモジュール変数と関数に対して名前空間XmlUtil_I {...}を定義しています。そのようにして、変数にアクセスするために、デバッガーでXmlUtil_I ::修飾を適用できます。この場合、「_ I」は、他で使用したいXmlUtilなどのパブリック名前空間と区別します。
真に匿名のアプローチと比較した場合、このアプローチの潜在的な欠点は、誰かが他のモジュールの名前空間修飾子を使用することにより、目的の静的スコープに違反する可能性があることです。ただし、それが大きな懸念事項かどうかはわかりません。
その目的での静的キーワードの使用は、C ++ 98標準では推奨されていません。静的の問題は、型定義には適用されないことです。また、異なるコンテキストでさまざまな方法で使用されるオーバーロードされたキーワードであるため、名前のない名前空間は少し物事を単純化します。
経験から、以前静的な関数を匿名の名前空間に入れるのはC ++の方法ですが、古いコンパイラではこれに問題がある場合があることに注意してください。現在、ターゲットプラットフォーム用にいくつかのコンパイラを使用していますが、最近のLinuxコンパイラは、匿名の名前空間に関数を配置しても問題ありません。
しかし、Solarisで実行されている古いコンパイラは、不特定の将来のリリースまでサポートされますが、それを受け入れる場合もあれば、エラーとしてフラグを立てる場合もあります。エラーは私を心配させるものではなく、それが受け入れるときにそうするかもしれないことです。そのため、全面的にモダンになるまで、静的な(通常はクラススコープの)関数を使用しており、匿名の名前空間を使用します。
さらに、次の例のように変数で静的キーワードを使用する場合:
namespace {
static int flag;
}
マッピングファイルには表示されません
質問を読んでいる間だけこの機能を知ったので、推測することしかできません。これは、ファイルレベルの静的変数に勝るいくつかの利点を提供するようです:
- 匿名の名前空間は互いに入れ子にすることができ、シンボルがエスケープできない複数のレベルの保護を提供します。
- 複数の匿名名前空間を同じソースファイルに配置して、同じファイル内に異なる静的レベルのスコープを事実上作成できます。
誰かが実際のコードで匿名の名前空間を使用したかどうかを知ることに興味があります。
匿名の名前空間と静的関数のコンパイラ固有の違いは、次のコードのコンパイルで確認できます。
#include <iostream>
namespace
{
void unreferenced()
{
std::cout << "Unreferenced";
}
void referenced()
{
std::cout << "Referenced";
}
}
static void static_unreferenced()
{
std::cout << "Unreferenced";
}
static void static_referenced()
{
std::cout << "Referenced";
}
int main()
{
referenced();
static_referenced();
return 0;
}
VS 2017でこのコードをコンパイルします(レベル4警告フラグ/ W4を指定して、警告C4505:参照されていないローカル関数が削除されました)および-Wunused-functionまたは-Wallフラグ付きのgcc 4.9は、VS 2017が生成するのは未使用の静的関数に関する警告。 gcc 4.9以降、およびclang 3.3以降では、名前空間内の参照されていない関数に関する警告と、未使用の静的関数に関する警告が生成されます。
個人的には、次の理由により、名前のない名前空間よりも静的関数を好みます。
-
関数の定義だけから、それがコンパイルされた翻訳単位に対してプライベートであることは明らかです。名前のない名前空間では、スクロールして検索し、関数が名前空間にあるかどうかを確認する必要があります。
名前空間の -
関数は、一部の(古い)コンパイラによってexternとして扱われる場合があります。 VS2017では、まだ外部にあります。このため、関数が名前のない名前空間にある場合でも、それらを静的にマークしたい場合があります。
-
静的関数はCまたはC ++で非常によく似ていますが、名前のない名前空間は明らかにC ++のみです。名前のない名前空間は、インデントが追加レベルを追加し、それが気に入らない場合:)
だから、関数の静的の使用廃止されなくなりました。