【C++】無名名前空間とは【用途と目的】


無名名前空間

無名名前空間(unnamed namespace)は外部リンケージを持たない名前空間です。これによってファイルスコープ内でのみ有効な局所的な変数や関数を宣言することが可能になります。

要するにstatic宣言された変数・関数と同じようなものを実現出来るわけです。
// 無名名前空間
namespace {
  int a = 1;
}

// 内部リンケージ
static int b = 2;

int main() {
  // いずれもファイル内ではアクセスできるが
  // 他のファイルからはアクセス出来ない
  printf("%d, %d", a, b);
}

ちなみにドキュメントやコンパイラによっては匿名名前空間(anonymous namespace)と呼ばれていることもありますが、無名名前空間(unnamed namespace)のほうが正式な名称です。

外部から読み取れない

無名名前空間内で宣言されたシンボルは一見グローバル空間に定義されているように見えると思うのですが(A.cpp参考)、実際は他のファイル・コンパイラ単位から実体を読み取れないという特徴があります。

A.cpp

// 無名名前空間
namespace {
  int a = 9;
}

// グローバル変数
int b = 8;

void test() {
  // int aは内部リンケージを持つためそのままアクセス出来る
  printf("%d, %d", a, b); // 9, 8
}

main.cpp

#include "A.hpp"

int main() {
  // A.cppのint aは外部リンケージを持たないため外部結合できない
  // ERROR: Undefined symbols for architecture x86_64: "_a", referenced from:  _main in main.o
  extern int a;

  // グローバル変数なので可能
  extern int b; // 8

  printf("%d, %d", a, b);
}

このように他の実装ファイル内でint aの名前解決が行えません。int bの場合は外部リンケージを持つため問題なく行えます。

これはある意味A.cppファイルのint aをA.cpp側で隠蔽することが出来たということでもあります。またこれは従来A.cpp内でstatic int a;と書いた際の挙動と同じものでもあるのです。

ちなみにC++の世界ではstaticの代わりに無名名前空間の利用が推奨されています。

名前衝突の回避

無名名前空間で宣言したシンボルも名前衝突の原因になることがあります。この場合、::修飾子による名前解決が必要になります。

namespace {
  int puts(const char* str) {
    return printf("DEBUG: %s\n", str);
  }
}

int main() {
  puts("a");   // ERROR: Call to 'puts' is ambiguous
  ::puts("b"); // stdio.h側の関数が呼ばれる
}

広告