プログラミング言語C++における無名名前空間(unnamed namespace)*1の、一見すると不思議な(実は根拠がある)定義に関するメモ。本記事はStack Overflow上での質問と回答内容に基づく。
C++言語仕様での定義
C++03 7.3.1.1/p1では、unnamed namespaceの振る舞いを次の通り定義する。C++11(N3337) 7.3.1.1/p1でも同一*2。
An unnamed-namespace-definition behaves as if it were replaced by
namespace unique { /* empty body */ } using namespace unique; namespace unique { namespace-body }where all occurrences of unique in a translation unit are replaced by the same identifier and this identifier differs from all other identifiers in the entire program. (snip)
下記のような単純な定義となっていない理由に、「最初のunnamed namespace内から、同namespaceで定義された名前(A
)をグローバル名前空間の修飾名(::A
)で参照」する問題が挙げられる。(具体例は後述)
namespace unique { namespace-body } using namespace unique;
次コードのunnamed namespaceは、一意な名前空間(仮にXXX
とする)を用いたコードと等価に(as if)振る舞う。つまり::A
によって、グローバル名前空間(global namespace)に取り込まれた名前XXX::A
に正しくアクセスできる。
namespace { typedef int A; ::A a0; } namespace { ::A a1; } // これは下記コードと等価 namespace XXX { } using namespace XXX; // 名前空間XXXをグローバル名前空間へ取り込む namespace XXX { typedef int A; ::A a0; // OK: "XXX::A a0;"と等価 } namespace XXX { } using namespace XXX; namespace XXX { ::A a1; // OK: "XXX::A a1;"と等価 }
仮に現行C++言語仕様とは異なる簡易定義とすると、前掲unnamed namespaceは下記コードと等価になる。つまりa0
の箇所ではill-formedとなるがa1
の箇所ではwell-formedとなり、全体として一貫性のない振る舞いになってしまう。
namespace XXX { typedef int A; ::A a0; // NG: この時点で::Aは存在しない! } using namespace XXX; namespace XXX { ::A a1; // OK: "::A"は"XXX::A"を指す } using namespace XXX;
gcc, Clang
gcc 4.5.4, 4.6.3, 4.7.2、およびclang 3.1でそれぞれコンパイルできることを確認。
namespace { typedef int A; ::A a0; }
VisualC++
MSVCにおけるunnamed namespaceの扱いについて、2012年11月現在のMSDNではC++言語仕様通りでない簡易定義で説明されている。実際にVisual Studio 2012(MSVC11)では下記コードがコンパイルエラーになる。
namespace { typedef int A; ::A a0; // NG // error C2039: 'A' : '`global namespace'' のメンバーではありません。 }
回避策として下記2パターンのように記述すれば、それぞれ期待通りにコンパイルが通ることを確認。
namespace { typedef int A; } namespace { ::A a0; // OK }
namespace {} // 中身は空 namespace { typedef int A; ::A a0; // OK }