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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6326805/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:56:33  来源:igfitidea点击:

Why "using namespace X;" is not allowed inside class/struct level?

c++namespacesusinglanguage-lawyer

提问by iammilind

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

Edit: Want to know motivation behind it.

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

采纳答案by Billy ONeal

I don't know exactly, but my guess is that allowing this at class scope could cause confusion:

我不确切知道,但我的猜测是在类范围内允许这样做可能会导致混淆:

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

Since there is no obvious way of doing this, the standard just says you can't.

由于没有明显的方法可以做到这一点,标准只是说你不能。

Now, the reason this is less confusing when we're talking namespace scopes:

现在,当我们谈论名称空间范围时,这不那么令人困惑的原因是:

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

回答by Adam Rosenfield

Because the C++ standard explicitly forbids it. From C++03 §7.3.4 [namespace.udir]:

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

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

A using-directiveshall not appear in class scope, but may appear in namespace scope or in block scope. [Note: when looking up a namespace-name in a using-directive, only namespace names are considered, see 3.4.6. ]

一个using指令将不会出现在类范围,但可能会出现在命名空间范围或块的范围。[注意:在 using 指令中查找命名空间名称时,只考虑命名空间名称,见 3.4.6。]

Why does the C++ standard forbid it? I don't know, ask a member of the ISO committee that approved the language standard.

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

回答by David Rodríguez - dribeas

I believe that the rationale is that it would probably be confusing. Currently, while processing a class level identifier, lookup will first search in the class scope and then in the enclosing namespace. Allowing the using namespaceat class level would have quite some side effects on how the lookup is now performed. In particular, it would have to be performed sometime between checking that particular class scope and checking the enclosing namespace. That is: 1) merge the class level and used namespace level lookups, 2) lookup the used namespace afterthe class scope but before any other class scope, 3) lookup the used namespace right before the enclosing namespace. 4) lookup merged with the enclosing namespace.

我相信理由是它可能会令人困惑。目前,在处理类级别标识符时,查找将首先在类范围中搜索,然后在封闭的命名空间中搜索。允许using namespace在类级别对现在如何执行查找会产生相当多的副作用。特别是,它必须在检查特定类范围和检查封闭命名空间之间的某个时间执行。那就是:1)合并级水平和使用的命名空间级别查找,2)查找所使用的命名空间的类范围,而在任何其他类范围,3)查找的封闭命名空间之前所使用的命名空间的权利。4) 查找与封闭的命名空间合并。

  1. This would make a big difference, where an identifier at class level would shadowany identifier in the enclosing namespace, but it would not shadowa usednamespace. The effect would be strange, in that access to the usednamespace from a class in a different namespace and from the same namespace would differ:
  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. Lookup right after this class scope. This would have the strange effect of shadowing base classes' members. The current lookup does not mix class and namespace level lookups, and when performing class lookup it will go all the way to the base classes beforeconsidering the enclosing namespace. The behavior would be surprising in that it would not consider the namespace in a similar level to the enclosing namespace. Again, the usednamespace would be prioritized over the enclosing namespace.
  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. Lookup right before the enclosing namespace. The problem with this approach is again that it would be surprising to many. Consider that the namespace is defined in a different translation unit, so that the following code cannot be seen all at once:
  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. Merge with the enclosing namespace. This would have the exact same effect that applying the usingdeclaration at the namespace level. It would not add any new value to that, but will on the other hand complicate lookup for compiler implementors. Namespace identifier lookup is now independent from where in the code the lookup is triggered. When inside a class, if lookup does not find the identifier at class scope it will fall back to namespace lookup, but that is exactly the same namespace lookup that is used in a function definition, there is no need to maintain new state. When the usingdeclaration is found at namespace level, the contents of the usednamespace are broughtinto that namespace for alllookups involving the namespace. If using namespacewas allowed at class level, there would be different outcomes for namespace lookup of the exact same namespace depending on where the lookup was triggered from, and that would make the implementation of the lookup much more complex for no additional value.
  1. 与封闭的命名空间合并。这与using在命名空间级别应用声明具有完全相同的效果。它不会为此添加任何新值,但另一方面会使编译器实现者的查找复杂化。命名空间标识符查找现在独立于在代码中触发查找的位置。在类内部,如果查找在类范围内找不到标识符,它将回退到名称空间查找,但这与函数定义中使用的名称空间查找完全相同,无需维护新状态。当using在命名空间级别找到声明时,使用的命名空间的内容将被带入该命名空间以进行所有涉及该命名空间的查找。如果using namespace在类级别被允许,对于完全相同的命名空间的命名空间查找将有不同的结果,具体取决于查找的触发位置,这将使查找的实现更加复杂,没有附加值。

Anyway, my recommendation is notto employ the using namespacedeclaration at all. It makes code simpler to reason with without having to keep all namespaces' contents in mind.

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

回答by naprimeroleg

I think it's a defect of the language. You may use workaround below. Keeping in mind this workaround, it is easy to suggest rules of names conflicts resolution for the case when the language will be changed.

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

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