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


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

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


1
@pst:C#にはusing namespace。のようなものはありません。C#でも同様のことが可能ですが、ファイルスコープでのみ可能です。C ++をusing namespace使用すると、ある名前空間を別の名前空間に組み込むことができます。
Billy ONeal 2011年


@ZachSaw、私はあなたの懸念を理解しています。関連性に基づいてQnを閉じようとしました。この投稿には、より客観的な回答と標準への参照が含まれているため、私はそれを開いたままにしました。過去に、私の古いQnの多くは新しいQnによって閉じられました..時々私によって時々他の人によって。この決定が適切でないと感じた場合は、ダイヤモンドModにフラグを立ててください。何恨みっこない。:-)
iammilind 2017年

@iammilindはTBHを気にすることができませんでした。SOは最近混乱しています。しかし、「正確にはわからない」で始まる投稿を回答としてマークすると、実際には「より客観的な回答と標準への参照」が含まれます。はは。
ザックソー2017年

@ZachSaw、私は受け入れられた答えだけでなく、全体的な投稿について話していました。はい、それは客観的ですが、標準的な見積もりはこの回答に含まれています。標準でも「名前空間の使用」が内部で許可されていない理由は正当化されないため、「わからない」で始まりますclass/struct。それは単に許可されていません。しかし、受け入れられた答えは、それを禁止する非常に論理的な論理的根拠を議論しています。つまり、どこを検討しHello::World、どこを検討するかWorld。それが疑問を解消することを願っています。
iammilind 2017年

回答:


35

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

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.
    }
}

5
+1、私はこの理由を考えましたが、同じことがusing namespace Hello;他の内部にnamespaceも当てはまります(そしてexternその内部で関数を宣言します)。
iammilind 2011年

10
紛らわしいとは思いません。C ++は当て推量ではありません。それが許可されていれば、C ++ ISO委員会は言語仕様で指定していたでしょう。そうすれば、混乱を招くとは言えません。そうでなければ、これでさえ混乱していると言うかもしれません:ideone.com/npOeD ...しかし、そのようなコーディングのルールは仕様で指定されています。
ナワズ2011年

1
@Nawaz:この言語のほとんどのユーザー。私はC ++が当て推量についてだとは決して言いませんでした。スペックが設計されるとき、それはほとんどのプログラマーが前もって期待する振る舞いで設計されていると言っています。そして、紙の上のルールがしばしばあり、標準的な試みが明確なことが、それは常に成功しません-混乱します。
Billy ONeal 2011年

6
最初の例では、次のようになります。Hello::World Blah::DoSomething()またはBlah::World Blah::DoSomething()(許可されている場合)、メンバー関数定義の戻り値の型は、言語のクラスのスコープ内にあるとは見なされないため、修飾する必要があります。交換の有効な例を考えusingtypedef Hello::World World;、クラススコープでは。したがって、そこに驚きはないはずです。
デビッド・ロドリゲス- dribeas

2
許可されれば、字句スコープレベルで適用されると思います。これは事実上驚きのない「明白な」解決策だと思います。
Thomas Eding 2012年

18

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

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

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

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


45
さらに別の技術的には正しいが役に立たない答え。最悪の種類。1)委員会だけでなく多くの人々が答えを知っています。2)委員会のメンバーがSOに参加します。3)答えがわからない場合(質問の精神を考えれば)、なぜ答えるのですか?
Catskul 2018

5
@Catskul:それは役に立たない答えではありません。規格がこれに明示的に対処し、禁止していることを知っておくと非常に便利です。最も賛成の答えが「正確にはわからない」で始まるのも皮肉です。また、「標準では禁止されています」は「コンパイラで許可されていないため許可されていません」と同じではありません。後者の場合、次のようなフォローアップの質問に答えられないためです。コンパイラに問題がありますか?コンパイラは標準に準拠していませんか?それは私が気付いていない他のいくつかの副作用ですか?など
アントノン

9

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

  1. これは大きな違いを生みます。クラスレベルの識別子は、それを囲む名前空間内の識別子をシャドウしますが、使用されている名前空間はシャドウしません。異なる名前空間のクラスと同じ名前空間からの使用済み名前空間へのアクセスが異なるという点で、効果は奇妙です。

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. このクラススコープの直後のルックアップ。これは、基本クラスのメンバーをシャドウイングするという奇妙な効果があります。現在のルックアップは、クラスレベルと名前空間レベルのルックアップを混合していません。クラスルックアップを実行すると、囲んでいる名前空間を検討するに、基本クラスまで実行されます。囲んでいる名前空間と同様のレベルの名前空間を考慮しないという点で、この動作は驚くべきものです。この場合も、使用される名前空間は、それを囲む名前空間よりも優先されます。

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宣言が名前空間レベルで発見され、内容に使用される名前空間がされもたらしたため、その名前空間の中に、すべての名前空間を含む検索。場合using namespace クラスレベルで許可された場合、ルックアップがトリガーされた場所に応じて、まったく同じ名前空間の名前空間ルックアップの結果が異なり、追加の値がないため、ルックアップの実装がはるかに複雑になります。

とにかく、私の推奨は、宣言をまったく採用しないことusing namespaceです。これにより、すべての名前空間の内容を念頭に置く必要がなく、コードの推論が簡単になります。


1
私は、使用すると暗黙の奇妙さが生じる傾向があることに同意します。ただし、一部のライブラリは、using存在する事実に基づいて設計される場合があります。深くネストされた長い名前空間で意図的に宣言することによって。たとえばglm、それを行い、クライアントがを使用するときに機能をアクティブ化/提示するために複数のトリックを使用しますusing
v.oddou 2016年

STLでも正しくusing namespace std::placeholders。cf en.cppreference.com/w/cpp/utility/functional/bind
v.oddou

@ v.oddou:namespace ph = std::placeholders;
デビッド・ロドリゲス- dribeas

1

これは、開放性閉鎖性のためにおそらく許可されていません

  • C ++のクラスと構造体は、常に閉じたエンティティです。それらは正確に1つの場所で定義されます(ただし、宣言と実装を分割することはできます)。
  • 名前空間は、任意に頻繁に開いたり、再度開いたり、拡張したりできます。

名前空間をクラスにインポートすると、次のような面白いケースが発生します。

namespace Foo {}

struct Bar { using namespace Foo; };

namespace Foo {
using Baz = int; // I've just extended `Bar` with a type alias!
void baz(); // I've just extended `Bar` with what looks like a static function!
// etc.
}

0

それは言語の欠陥だと思います。以下の回避策を使用できます。この回避策を念頭に置いて、言語が変更される場合の名前の競合の解決を提案するのは簡単です。

namespace Hello
{
    typedef int World;
}
// surround the class (where we want to use namespace Hello)
// by auxiliary namespace (but don't use anonymous namespaces in h-files)
namespace Blah_namesp {
using namespace Hello;

class Blah
{
public:
    World DoSomething1();
    World DoSomething2();
    World DoSomething3();
};

World Blah::DoSomething1()
{
}

} // namespace Blah_namesp

// "extract" class from auxiliary namespace
using Blah_namesp::Blah;

Hello::World Blah::DoSomething2()
{
}
auto Blah::DoSomething3() -> World
{
}

説明をお願いします。
KishanBharda19年

はい、私はいくつかのコメントを追加している
naprimeroleg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.