web-dev-qa-db-ja.com

名前のない名前空間が使用される理由とその利点は何ですか?

新しいC++ソフトウェアプロジェクトに参加したばかりで、デザインを理解しようとしています。このプロジェクトは、名前のない名前空間を頻繁に使用します。たとえば、クラス定義ファイルで次のようなことが発生する場合があります。

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

名前のない名前空間を使用する可能性のある設計上の考慮事項は何ですか?長所と短所は何ですか?

215
Scottie T

(以下では、物事はもはやC++ 11には当てはまらないが、C++ 03には当てはまるものです。C++ 11はもうほとんど違いはありません(もしあれば、それらは単なる言語弁護士の違いです。思い出せない))。

名前のない名前空間は、識別子変換ユニットをローカルにするユーティリティです。これらは、名前空間の翻訳単位ごとに一意の名前を選択するかのように動作します。

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

空のボディを使用する追加のステップは重要です。そのため、usingディレクティブが既に実行されているため、ネームスペースボディ内で、そのネームスペースで定義されている::nameなどの識別子を既に参照できます。

つまり、複数の翻訳単位に存在できる(たとえば)helpと呼ばれる無料の関数を使用でき、リンク時に衝突することはありません。その効果は、識別子の宣言に含めることができるCで使用されるstaticキーワードを使用するのとほぼ同じです。名前のない名前空間は優れた代替手段であり、タイプ変換ユニットをローカルにすることさえできます。

namespace { int a1; }
static int a2;

両方のaは翻訳単位ローカルであり、リンク時に衝突しません。ただし、違いは、匿名名前空間のa1が一意の名前を取得することです。

Comeau-computingの優れた記事を読む なぜstaticの代わりに名前のない名前空間が使用されるのか?Archive.org mirror )。

171

匿名の名前空間に何かがあるということは、これがローカルにあることを意味します 翻訳単位 (.cppファイルとそのすべてのインクルード) 1つの定義ルール (ODR)。

これは、静的なグローバル変数または静的な関数を持つCの方法と同じですが、クラス定義にも使用できます(C++ではstaticではなく使用する必要があります)。

同じファイル内のすべての匿名名前空間は同じ名前空間として扱われ、異なるファイル内のすべての匿名名前空間は区別されます。匿名名前空間は次のものと同等です。

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
63
Motti

この例は、参加したプロジェクトの人々が匿名の名前空間を理解していないことを示しています:)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

constオブジェクトはすでに静的リンケージを持っているため、これらは匿名の名前空間にある必要はありません。したがって、別の翻訳単位の同じ名前の識別子と競合することはできません。

    bool getState(userType*,otherUserType*);
}

そして、これは実際には悲観的です:getState()には外部リンケージがあります。シンボルテーブルを汚染しないため、通常は静的リンケージを選択することをお勧めします。書く方が良い

static bool getState(/*...*/);

ここに。私は同じtrapに陥りました(ファイル静的は匿名の名前空間を支持して非難されることを示唆する標準の文言があります)が、KDEのような大規模なC++プロジェクトで働いていると、多くの人々があなたの頭を正しい方向に向けます再び:)

12

この質問に対する他の回答に加えて、匿名のネームスペースを使用するとパフォーマンスも向上します。名前空間内のシンボルは外部リンケージを必要としないため、コンパイラは名前空間内のコードの積極的な最適化をより自由に実行できます。たとえば、ループ内で複数回呼び出される関数は、コードサイズに影響を与えることなくインライン化できます。

たとえば、私のシステムでは、匿名ネームスペースが使用されている場合、次のコードは実行時間の約70%を使用します(x86-64 gcc-4.6.3および-O2。 2回)。

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}
12
xioxox

匿名の名前空間は、囲まれた変数、関数、クラスなどをそのファイル内でのみ使用可能にします。あなたの例では、グローバル変数を回避する方法です。実行時またはコンパイル時のパフォーマンスの違いはありません。

「この変数、関数、クラスなどをパブリックにするかプライベートにするか」を除けば、それほどメリットもデメリットもありません。

11
Max Lybbert

名前のない名前空間は、クラス、変数、関数、およびオブジェクトのアクセスを、それが定義されているファイルに制限します。名前のない名前空間機能は、C/C++のstaticキーワードに似ています。
staticキーワードは、グローバル変数と関数が定義されているファイルへのアクセスを制限します。
名前のないネームスペースとstaticキーワードには違いがあります。これは、名前のないネームスペースが静的よりも優れているためです。 staticキーワードは、変数、関数、およびオブジェクトで使用できますが、ユーザー定義のクラスでは使用できません。
例えば:

static int x;  // Correct 

しかし、

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

ただし、名前のない名前空間でも同じことが可能です。例えば、

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
9
Sachin