为什么“使用名称空间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

2

@ZachSaw,我了解您的关注。尝试根据相关性关闭Qn。由于这篇文章包含了更多客观的答案和对标准的引用,因此我一直保持开放。过去,我的许多较老的Qn都被较新的Qn关闭了,有时被我关闭,有时被其他人关闭。如果您认为此决定不合适,请标记为钻石Mods。别往心里放。:-)
iammilind

@iammilind一点也不关心TBH。这几天真是一团糟。但是,将以“我不完全知道”开头的帖子标记为答案实际上包含“更客观的答案和对标准的引用”。哈哈。
Zach Saw

@ZachSaw,我不仅在谈论接受的答案,还在谈论整个帖子。是的,这是客观的,但标准答案包含在此答案中。它以“我不知道”开头,因为即使在标准情况下,也没有理由说明为什么内部不允许使用“使用名称空间” class/struct。只是不允许这样做。但是,被接受的答案确实讨论了禁止它的非常合乎逻辑的理由。即在哪里考虑Hello::World和在哪里考虑World。希望这消除了疑问。
iammilind

Answers:


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 ...,但随后在规范中指定了此类编码的规则。
Nawaz

1
@Nawaz:该语言的大多数用户。我从没说过C ++是关于猜测的。我说的是,在设计规范时,它是用大多数程序员期望的行为设计的。在纸上的规则往往混淆-标准尝试是明确的,但它并不总是成功。
Billy ONeal

6
在第一个示例中,它应该是:Hello::World Blah::DoSomething()Blah::World Blah::DoSomething()(如果允许),则不认为成员函数定义的返回类型在该语言的类范围内,因此必须对其进行限定。考虑usingtypedef Hello::World World;at类范围替换at的有效示例。因此,那里应该没有惊喜。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2011年

2
如果允许的话,我相信它将在词汇范围上应用。我认为这是“显而易见的”解决方案,几乎没有任何惊喜。
Thomas Eding

18

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

使用指令:
    使用命名空间:: opt 嵌套名称说明符opt  namespace-name ;

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

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


45
另一个技术上正确但无用的答案;最糟糕的一种。1)不仅仅是委员会知道答案的人。2)委员会成员参加SO 3)如果您不知道答案(考虑到问题的实质),为什么还要回答?
Catskul '18年

5
@Catskul:这不是一个无用的答案。知道该标准明确地解决了这个问题并禁止它是非常有用的。具有讽刺意味的是,最受支持的答案以“我不清楚”开头。另外,“标准禁止它”与“由于编译器不允许它是不允许的”不同,因为后一种情况无法回答诸如以下的问题:我的编译器有问题吗?编译器不符合标准吗?这是我不知道的其他事情的副作用吗?等
antonone

9

我认为,这样做的理由可能是令人困惑的。当前,在处理类级别标识符时,查找将首先在类范围内搜索,然后在封闭的名称空间中搜索。允许using namespaceat类级别将对现在如何执行查找产生相当大的副作用。特别是,它必须在检查特定类范围和检查封闭名称空间之间的某个时间执行。那就是: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

甚至在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 ++中的类和结构始终是封闭的实体。它们仅在一个位置定义(尽管您可以拆分声明和实现)。
  • 可以任意打开,重新打开和扩展名称空间。

将名称空间导入到类中会导致如下有趣的情况:

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

你能补充些解释吗?
Kishan Bharda

是的,我添加了一些评论
naprimeroleg
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.