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

iam*_*ind 77 c++ namespaces using language-lawyer

class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}
Run Code Online (Sandbox Code Playgroud)

编辑:想知道背后的动机.

Bil*_*eal 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?
}
Run Code Online (Sandbox Code Playgroud)

由于没有明显的方法这样做,标准只是说你做不到.

现在,当我们讨论命名空间范围时,这不会引起混淆:

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不认为它令人困惑.C++不是猜测.如果允许,那么C++ ISO委员会将在语言规范中指定.然后你不会说它令人困惑.否则有人可能会说这甚至令人困惑:http://www.ideone.com/npOeD ...但是规范中规定了这种编码的规则. (8认同)
  • +1,我想到了这个原因,但是同样的事情适用于`使用命名空间Hello;`在其他`namespace`里面(并在其中声明`extern`函数). (5认同)
  • 在第一个例子中,它应该是:`Hello :: World Blah :: DoSomething()`或`Blah :: World Blah :: DoSomething()`(如果允许的话),成员函数定义的返回类型是不被认为属于该语言的班级范围,因此必须具备资格.考虑在类范围内用`typedef Hello :: World World;`替换`using`的有效示例.所以那里应该没有意外. (4认同)
  • 如果允许,我相信它将应用于词法范围级别。我认为这是“明显”的解决方案,几乎没有任何惊喜。 (2认同)

Ada*_*eld 17

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

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

一个using指令将不会出现在类范围,但可能会出现在命名空间范围或块的范围.[注意:在using-directive中查找namespace-name时,只考虑名称空间名称,请参阅3.4.6.]

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

  • 又一个技术上正确但无用的答案; 最糟糕的一种.1)比委员会更多的人知道答案.2)委员会成员参加SO 3)如果你不知道答案(鉴于问题的精神)为什么要回答? (28认同)
  • @Catskul:这不是一个无用的答案。知道该标准明确解决并禁止它是非常有用的。同样具有讽刺意味的是,获得最多支持的答案以“我不太清楚”开头。此外,“标准禁止它”与“不允许它,因为编译器不允许它”不同,因为后一种情况不会回答后续问题,例如:我的编译器有问题吗?编译器不符合标准吗?这是我不知道的其他事情的副作用吗?ETC。 (7认同)
  • 虽然这不是“最糟糕的”答案,但它没有回答这个问题:为什么 C++ 禁止它。OP 已经确定 C++ 禁止它。 (2认同)
  • @antonone 这个问题承认目前这是被禁止的,并明确询问其背后的动机。因此,这个答案是不充分的,没有回答问题。 (2认同)

Dav*_*eas 8

我认为理由是它可能会令人困惑.目前,在处理类级别标识符时,lookup将首先在类范围内搜索,然后在封闭的命名空间中搜索.允许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
   }
};
Run Code Online (Sandbox Code Playgroud)
  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()
   }
};
Run Code Online (Sandbox Code Playgroud)
  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
   }
};
Run Code Online (Sandbox Code Playgroud)
  1. 与封闭的命名空间合并.这与using在命名空间级别应用声明具有完全相同的效果.它不会为此添加任何新值,但另一方面会使编译器实现者的查找复杂化.命名空间标识符查找现在独立于触发查找的代码中的位置.在类中,如果查找在类范围内找不到标识符,它将回退到命名空间查找,但这与函数定义中使用的命名空间查找完全相同,不需要维护新状态.当using声明是在命名空间层次中,该内容使用的命名空间被带到到该命名空间为所有涉及的命名空间查找.如果using namespace在类级别允许,则根据触发查找的位置,对完全相同的命名空间进行命名空间查找会有不同的结果,这将使查找的实现更加复杂,无需额外的值.

无论如何,我的建议是不要使用using namespace声明.它使代码更容易理解,而不必记住所有名称空间的内容.


Dan*_*elS 7

由于开放性封闭性,这可能是不允许的。

  • 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.
}
Run Code Online (Sandbox Code Playgroud)

  • 或者我们可以不使用导入的名称定义类成员。让这个构造将“命名空间 Foo”添加到“struct Bar”类型定义内的所有代码的搜索顺序中,就像将该行放在每个内联成员函数体中一样,除了它对于大括号或等于也将是活动的但它仍然会在右大括号处过期,与成员函数体内的“using namespace”相同。不幸的是,现在似乎没有任何方法可以在大括号或等于初始化程序中使用 Koenig-with-fallback 查找而不污染封闭的名称空间。 (4认同)