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


242

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

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

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

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

回答:


189

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

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

空の本文を使用する追加の手順は重要です::name。usingディレクティブが既に実行されているため、名前空間の本文内で、その名前空間で定義されているような識別子をすでに参照できます。

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

namespace { int a1; }
static int a2;

どちらもaローカルの翻訳単位であり、リンク時に競合しません。しかし、違いはa1、匿名の名前空間が一意の名前を取得することです。

comeau-computingの優れた記事を読んでください。静的ではなく名前のない名前空間が使用されるのはなぜですか?Archive.orgミラー)。


との関係を説明しstaticます。と比較してもらえます__attribute__ ((visibility ("hidden")))か?
phinz

74

匿名の名前空間に何かがあることは、それがこの変換単位(.cppファイルとそのすべてのインクルード)に対してローカルであることを意味します。つまり、同じ名前の別のシンボルが他の場所で定義されている場合、One Definition Rule(ODR)に違反しません。

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

名前空間は、クラス、変数、関数、およびオブジェクトのアクセスを、それが定義されているファイルに制限します。名前空間の機能はstatic、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

13

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

たとえば、私のシステムでは、匿名の名前空間が使用されている場合、次のコードは実行時間の約70%を占めます(x86-64 gcc-4.6.3および-O2; add_valの追加コードにより、コンパイラーが含めたくないことに注意してくださいそれを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;
}

5
真実であるには良すぎる-私はこのセグメントをgcc 4-1-2で試して、O3最適化を使用し、名前空間ステートメントを使用した場合と使用しない場合:->同じ時間(3秒、-O3を使用、4秒を-O3を使用)
Theo

2
このコードは、bとadd_valをメインにインライン化しないようにコンパイラーを説得しようとする意図的に複雑でした。O3最適化では、コードの膨張にかかるコストに関係なく、多くのインライン化を使用します。ただし、O3がadd_valをインライン化しない可能性のある関数はまだあります。add_valをより複雑にするか、さまざまな状況でmainから複数回呼び出すことを試みることができます。
xioxox

5
@ダニエル:何が足りないの?読んだように、あなたは自分-O3自身と比較して、3秒と4秒は「同じ時間」だと言った。これらはどちらも少し意味がありません。私は本当の説明があると思うが、それは何ですか?
underscore_d 2015

@underscore_d回答では、-O2ではなく-O3が使用されたと述べています。最適化レベルが異なると動作が異なる場合があります。また、コンパイラーのバージョンが異なると、動作が異なる可能性があります(答えが古くなる可能性があります)
Paul Stelian

1
@PaulStelian私はそれを知っていますが、私はxioxoxの回答ではなく、Theoのコメントに返信していたことがかなり明らかです(彼の名前が変更されたか、私が混乱した場合でも)
underscore_d

12

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

ここに。私は同じ罠に陥りました(標準では、ファイルスタティックは匿名の名前空間のために非推奨になっていることを示唆する文言があります)が、KDEのような大規模なC ++プロジェクトで作業すると、多くの人が正しい方法で頭を向けます再び周り:)


10
C ++ 11の無名の名前空間は、内部結合(標準またはセクション3.5有するためen.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces
エミールVrijdags

11
それでも、それはその意味を視覚的にリマインダとして、一つにそれらを置くことと、(さらに)些細削除するためにそれを作るために悪くはない-確か技術的には、「これらは、匿名の名前空間にある必要はありません」constらしさを後で必要に応じて。それはOPのチームが何も「理解していない」ことを意味しているとは思えません!また、言及されているように、C ++ 11以降では、外部リンケージを持つ匿名の名前空間の関数に関するビットが間違っています。私の理解により、以前は外部リンケージが必要であったテンプレート引数の問題が修正されたため、名前のない名前空間(テンプレート引数を含むことができる)に内部リンケージを持たせることができました。
underscore_d 2016

11

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

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


2
パフォーマンスの違いがある可能性があります-私の回答はこちらをご覧ください。これにより、コンパイラはコードをより適切に最適化できます。
xioxox 2014

2
あなたはポイントを持っています。少なくとも今日のC ++までは。ただし、C ++ 98 / C ++ 03では、テンプレート引数として使用するために外部リンケージが必要です。匿名の名前空間にあるものはテンプレート引数として使用できるため、ファイルの外部から参照する方法がなくても、(少なくともC ++ 11以前では)外部リンケージが存在します。規格はルールが施行されたかのように行動することだけを要求しているので、それをいじくる能力があったかもしれないと思う。ルールを厳密に適用せずにそれを実行できる場合もあります。
Max Lybbert 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.