web-dev-qa-db-ja.com

「名前空間Xを使用する」理由クラス/構造レベル内では許可されていませんか?

class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

編集:その背後にある動機を知りたい。

75
iammilind

正確にはわかりませんが、クラススコープでこれを許可すると混乱が生じる可能性があります。

namespace Hello
{
    typedef int World;
}

class Blah
{
    using namespace Hello;
public:
    World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
    //Is the using namespace valid in here?
}

これを行う明白な方法はないので、規格は単にできないとだけ言っています。

さて、これが名前空間のスコープについて話しているときの混乱が少なくなる理由:

namespace Hello
{
    typedef int World;
}

namespace Other
{
    using namespace Hello;
    World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
    //We're outside of a namespace; obviously the using namespace doesn't apply here.
    //EDIT: Apparently I was wrong about that... see comments. 
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
    //Ditto
}

namespace Other
{
    //namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
    //Therefore this is unambiguiously right
    World DoSomething()
    {
        //We're inside the namespace, obviously the using namespace does apply here.
    }
}
30
Billy ONeal

C++標準では明示的に禁止されているためです。 C++ 03§7.3.4[namespace.udir]から:

使用ディレクティブ:
名前空間の使用::optネストされた名前指定子opt名前空間名 ; 

sing-directiveはクラススコープには表示されませんが、ネームスペーススコープまたはブロックスコープに表示される場合があります。 [注:usingディレクティブで名前空間名を検索する場合、名前空間名のみが考慮されます。3.4.6を参照してください。 ]

C++標準で禁止されているのはなぜですか?私は知りません、言語標準を承認したISO委員会のメンバーに尋ねてください。

16
Adam Rosenfield

その理由は、おそらく混乱を招くことだと思います。現在、クラスレベルの識別子の処理中に、ルックアップは最初にクラススコープで検索し、次にそれを囲む名前空間で検索します。 using namespaceクラスレベルでは、検索の実行方法にかなりの副作用があります。特に、特定のクラススコープをチェックしてから、それを囲む名前空間をチェックするまでの間に実行する必要があります。つまり、1)クラスレベルと使用されたネームスペースレベルのルックアップをマージする、2)使用されたネームスペースをルックアップするafterクラススコープであるが他のクラススコープの前3)囲んでいる名前空間の直前で使用されている名前空間を検索します。 4)囲んでいる名前空間とマージされたルックアップ。

  1. これは、クラスレベルの識別子がshadow囲んでいる名前空間内の任意の識別子である場合に大きな違いを生じますが、そうではありませんshadowused名前空間。異なる名前空間のクラスと同じ名前空間からのused名前空間へのアクセスが異なるため、効果は奇妙です。

namespace A {
   void foo() {}
   struct B {
      struct foo {};
      void f() {
         foo();      // value initialize a A::B::foo object (current behavior)
      }
   };
}
struct C {
   using namespace A;
   struct foo {};
   void f() {
      foo();         // call A::foo
   }
};
  1. このクラススコープの直後のルックアップ。これには、基本クラスのメンバーをシャドウイングするという奇妙な効果があります。現在のルックアップではクラスとネームスペースレベルのルックアップが混在せず、クラスルックアップを実行するとき、それは基底クラスbeforeに行きます。 。この振る舞いは、名前空間を囲む名前空間と同じレベルの名前空間を考慮しないという点で驚くべきことです。繰り返しますが、used名前空間は、それを囲む名前空間よりも優先されます。

namespace A {
   void foo() {}
}
void bar() {}
struct base {
   void foo();
   void bar();
};
struct test : base {
   using namespace A;
   void f() {
      foo();           // A::foo()
      bar();           // base::bar()
   }
};
  1. 囲んでいる名前空間の直前の参照。このアプローチの問題点は、多くの人にとって驚くべきことです。名前空間は別の翻訳単位で定義されているため、次のコードを一度に表示することはできません。

namespace A {
   void foo( int ) { std::cout << "int"; }
}
void foo( double ) { std::cout << "double"; }
struct test {
   using namespace A;
   void f() {
      foo( 5.0 );          // would print "int" if A is checked *before* the
                           // enclosing namespace
   }
};
  1. 囲んでいる名前空間とマージします。これは、名前空間レベルでusing宣言を適用するのとまったく同じ効果があります。それは新しい値を追加しませんが、一方でコンパイラ実装者の検索を複雑にします。ネームスペース識別子のルックアップは、コード内のルックアップがトリガーされる場所から独立しています。クラス内で、ルックアップがクラススコープで識別子を見つけられない場合、ネームスペースルックアップにフォールバックしますが、それは関数定義で使用されるネームスペースルックアップとまったく同じであるため、新しい状態を維持する必要はありません。 using宣言が名前空間レベルで見つかった場合、used名前空間の内容はbrought 名前空間を含むallルックアップのためにその名前空間に。 using namespaceはクラスレベルで許可され、ルックアップのトリガー元に応じてまったく同じネームスペースのネームスペースルックアップの結果が異なるため、値の追加なしでルックアップの実装がより複雑になります。

とにかく、私の推奨事項はnotを使用するusing namespace宣言。すべての名前空間の内容を考慮する必要なく、コードを推論するのが簡単になります。