为什么“使用命名空间X;”在类/结构级别内是不允许的?

时间:2011-06-13 04:56:31

标签: c++ namespaces using language-lawyer

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

编辑:想了解背后的动机。

5 个答案:

答案 0 :(得分:30)

我不确切知道,但我的猜测是在课堂范围内允许这种情况会引起混淆:

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

答案 1 :(得分:15)

因为C ++标准明确禁止它。从C ++03§7.3.4[namespace.udir]:

using-directive:
    using namespace ::opt nested-name-specifieropt namespace-name ;
  

using-directive 不应出现在类范围内,但可能出现在命名空间范围或块范围内。 [注意:在using-directive中查找namespace-name时,只考虑名称空间名称,请参阅3.4.6。 ]

为什么C ++标准禁止它?我不知道,要求ISO委员会成员批准语言标准。

答案 2 :(得分:8)

我认为理由是它可能会令人困惑。目前,在处理类级别标识符时,lookup将首先在类范围中搜索,然后在封闭的命名空间中搜索。在类级别允许using namespace会对现在执行查找的方式产生一些副作用。特别是,必须在检查特定类范围和检查封闭命名空间之间执行某些操作。即:1)合并类级别并使用命名空间级别查找,2)在类范围之后但在任何其他类范围之前查找已使用的命名空间,3)在封闭命名空间之前查找已使用的命名空间。 4)查找与封闭的命名空间合并。

  1. 这会产生很大的不同,其中类级别的标识符 shadow 封闭命名空间中的任何标识符,但它不会 shadow 使用< / em>命名空间。效果会很奇怪,因为从不同命名空间和同一命名空间中的类访问使用的命名空间会有所不同:
  2. 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. 在此课程范围之后立即查找。这会产生阴影基类成员的奇怪效果。当前查找不会混合类和命名空间级别查找,并且在执行类查找时,它将在考虑封闭命名空间之前一直到基类。这种行为会令人惊讶,因为它不会将命名空间视为与封闭命名空间类似的级别。同样,使用的命名空间将优先于封闭的命名空间。
    2. 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. 在封闭的命名空间之前查找。这种方法的问题再次是许多人会感到惊讶。考虑到命名空间是在不同的翻译单元中定义的,因此不能同时看到以下代码:
      2. 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,则根据触发查找的位置,对完全相同的命名空间进行命名空间查找会有不同的结果,这会使查找的实现更复杂,没有其他值
        2. 无论如何,我建议完全使用using namespace声明。它使代码更容易理解,而不必记住所有名称空间的内容。

答案 3 :(得分:1)

这可能是不允许的,因为开放性封闭性。

  • C ++中的类和结构始终是封闭的实体。它们仅在一个位置定义(尽管您可以拆分声明和实现)。
  • 命名空间可以经常任意打开,重新打开和扩展。

将名称空间导入类会导致类似这样的有趣情况:

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

答案 4 :(得分:-1)

我认为这是语言的缺陷。您可以使用下面的解决方法。牢记此变通办法,对于更改语言的情况,很容易建议名称规则与解析冲突。

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