为什么类/结构层中不允许“使用名称空间X;”?

时间:2021-04-21 03:12:53
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.

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

4 个解决方案

#1


27  

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

#2


23  

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 ::optnested-name-specifieroptnamespace-name ;

A using-directive shall 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. ]

使用指令不能出现在类范围中,但可以出现在名称空间范围或块范围中。[注意:在使用指令中查找名称空间名称空间时,只考虑名称空间名称,参见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++标准禁止它?我不知道,问问国际标准化组织的一个成员,他批准了语言标准。

#3


8  

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 namespace at 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 after the 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.

我认为其基本原理是它可能会令人困惑。目前,在处理类级别标识符时,查找将首先在类范围内搜索,然后在封闭的名称空间中搜索。在类级别上允许使用名称空间将对查找的执行方式产生相当大的副作用。特别是,在检查特定的类作用域和检查封闭的名称空间之间必须执行它。也就是说:1)合并类级别和使用的名称空间级别查找,2)在类范围之后查找使用的名称空间,但是在任何其他类范围之前查找,3)在封闭的名称空间之前查找使用的名称空间。4)与封闭名称空间合并的查找。

  1. This would make a big difference, where an identifier at class level would shadow any identifier in the enclosing namespace, but it would not shadow a used namespace. The effect would be strange, in that access to the used namespace from a class in a different namespace and from the same namespace would differ:
  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. 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 before considering 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 used namespace would be prioritized over the enclosing namespace.
  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. 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:
  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. Merge with the enclosing namespace. This would have the exact same effect that applying the using declaration 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 using declaration is found at namespace level, the contents of the used namespace are brought into that namespace for all lookups involving the namespace. If using namespace was 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.
  2. 与封闭的名称空间合并。这将产生与在命名空间级别应用using声明完全相同的效果。它不会增加任何新的价值,但会使编译器实现者的查找变得复杂。名称空间标识符查找现在独立于在代码中触发查找的位置。在类中,如果查找在类范围内没有找到标识符,它将退回到名称空间查找,但这与函数定义中使用的名称空间查找完全相同,因此不需要维护新的状态。当在名称空间级别找到using声明时,使用的名称空间的内容将被带入到该名称空间中,以便所有涉及名称空间的查找。如果允许在类级别上使用名称空间,那么同一名称空间的名称空间查找会有不同的结果,这取决于查找是从哪里触发的,这将使查找的实现变得更加复杂,不需要额外的值。

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

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

#4


-2  

But why do you need it? Just put the class in the separate file and include namespaces you want in case if you don't want other classes reach them.

但你为什么需要它呢?只要将类放在单独的文件中,并包含您想要的名称空间,以防万一您不希望其他类到达它们。

#1


27  

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

#2


23  

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 ::optnested-name-specifieroptnamespace-name ;

A using-directive shall 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. ]

使用指令不能出现在类范围中,但可以出现在名称空间范围或块范围中。[注意:在使用指令中查找名称空间名称空间时,只考虑名称空间名称,参见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++标准禁止它?我不知道,问问国际标准化组织的一个成员,他批准了语言标准。

#3


8  

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 namespace at 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 after the 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.

我认为其基本原理是它可能会令人困惑。目前,在处理类级别标识符时,查找将首先在类范围内搜索,然后在封闭的名称空间中搜索。在类级别上允许使用名称空间将对查找的执行方式产生相当大的副作用。特别是,在检查特定的类作用域和检查封闭的名称空间之间必须执行它。也就是说:1)合并类级别和使用的名称空间级别查找,2)在类范围之后查找使用的名称空间,但是在任何其他类范围之前查找,3)在封闭的名称空间之前查找使用的名称空间。4)与封闭名称空间合并的查找。

  1. This would make a big difference, where an identifier at class level would shadow any identifier in the enclosing namespace, but it would not shadow a used namespace. The effect would be strange, in that access to the used namespace from a class in a different namespace and from the same namespace would differ:
  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. 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 before considering 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 used namespace would be prioritized over the enclosing namespace.
  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. 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:
  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. Merge with the enclosing namespace. This would have the exact same effect that applying the using declaration 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 using declaration is found at namespace level, the contents of the used namespace are brought into that namespace for all lookups involving the namespace. If using namespace was 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.
  2. 与封闭的名称空间合并。这将产生与在命名空间级别应用using声明完全相同的效果。它不会增加任何新的价值,但会使编译器实现者的查找变得复杂。名称空间标识符查找现在独立于在代码中触发查找的位置。在类中,如果查找在类范围内没有找到标识符,它将退回到名称空间查找,但这与函数定义中使用的名称空间查找完全相同,因此不需要维护新的状态。当在名称空间级别找到using声明时,使用的名称空间的内容将被带入到该名称空间中,以便所有涉及名称空间的查找。如果允许在类级别上使用名称空间,那么同一名称空间的名称空间查找会有不同的结果,这取决于查找是从哪里触发的,这将使查找的实现变得更加复杂,不需要额外的值。

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

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

#4


-2  

But why do you need it? Just put the class in the separate file and include namespaces you want in case if you don't want other classes reach them.

但你为什么需要它呢?只要将类放在单独的文件中,并包含您想要的名称空间,以防万一您不希望其他类到达它们。