为什么在类/结构级别不允许"using namespace X;"?
Why is "using namespace X;" not allowed at class/struct level?
class C {
using namespace std; // error
};
namespace N {
using namespace std; // ok
}
int main () {
using namespace std; // ok
}
我想知道它背后的动机。
我不确切知道,但我的猜测是在类范围内允许这样做可能会导致混淆:
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.
}
}
因为C++标准明确禁止这样做。 从 C++03 §7.3.4 [命名空间.udir]:
使用指令: 使用命名空间 ::opt嵌套名称说明符opt命名空间名称;
using-指令不应出现在类作用域中,但可以出现在命名空间作用域或块作用域中。[注意:在 using-指令中查找命名空间名称时,仅考虑命名空间名称,请参阅 3.4.6。
为什么C++标准禁止它? 我不知道,问问批准语言标准的ISO委员会成员。
理由是它可能会令人困惑。目前,在处理类级别标识符时,查找将首先在类范围内搜索,然后在封闭命名空间中搜索。允许在类级别进行using namespace
会对现在执行查找的方式产生相当多的副作用。特别是,必须在检查特定类范围和检查封闭命名空间之间的某个时间执行它。也就是说:1( 合并类级别和使用的命名空间级别查找,2( 在类作用域之后但在任何其他类作用域之前查找使用的命名空间,3( 在封闭命名空间之前查找使用的命名空间。4( 查找与封闭命名空间合并。
- 这将产生很大的不同,其中类级别的标识符将隐藏封闭命名空间中的任何标识符,但它不会隐藏使用的命名空间。效果会很奇怪,因为从不同命名空间中的类和从同一命名空间访问使用的命名空间会有所不同:
.
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
}
};
- 紧跟在此类作用域之后查找。这将产生隐藏基类成员的奇怪效果。当前查找不会混合类和命名空间级别的查找,并且在执行类查找时,它将在考虑封闭命名空间之前一直到基类。该行为令人惊讶,因为它不会将命名空间视为与封闭命名空间类似的级别。同样,使用的命名空间将优先于封闭命名空间。
.
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()
}
};
- 在封闭命名空间之前查找。这种方法的问题再次在于,它会让许多人感到惊讶。请考虑命名空间在不同的翻译单元中定义,因此无法一次看到以下代码:
.
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
}
};
- 与封闭命名空间合并。这将具有与在命名空间级别应用
using
声明完全相同的效果。它不会为此增加任何新值,但另一方面会使编译器实现器的查找复杂化。命名空间标识符查找现在独立于代码中触发查找的位置。在类内部时,如果查找在类范围内找不到标识符,它将回退到命名空间查找,但这与函数定义中使用的命名空间查找完全相同,因此无需维护新状态。在命名空间级别找到using
声明时,已使用命名空间的内容将引入该命名空间,以便进行涉及该命名空间的所有查找。如果在类级别允许using namespace
,则根据触发查找的位置,完全相同命名空间的命名空间查找将有不同的结果,这将使查找的实现更加复杂,没有额外的价值。
无论如何,我的建议是根本不使用using namespace
声明。它使代码更易于推理,而无需牢记所有命名空间的内容。
这可能是不允许的,因为开放性与封闭性。
- 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.
}
我认为这是语言的缺陷。您可以使用以下解决方法。请记住此解决方法,很容易在更改语言的情况下建议名称冲突解决方案规则。
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
{
}
在类中使用using namespace
,但你可以做的是简单地使用#define
,然后在结构内部使用#undef
。它的行为方式与namespace a = b;
完全相同
struct foo
{
#define new_namespace old_namespace
void foo2()
{
new_namespace::do_something();
}
#undef new_namespace
};
- "using namespace std;"在C++的作用是什么?
- 为什么我需要做'using namespace std'而不是"使用 std::cout"?
- 'using namespace'实现细节的便捷方法(仅标头库)?
- C++中"typedef"、"using"、"namespace"和"using namespace"有什么区别?
- "using namespace"子句在什么范围内有效?
- 如何找到"using namespace std"的违规用法?
- 在实现文件中,我们应该更喜欢"using namespace"指令还是将实现包装在命名空间 { } 中?
- 如何根据对象名称将'using namespace'添加到类定义中?
- 没有任何 #include "using namespace std;"?
- 用于定义全局函数"using namespace"
- 'using namespace'如何在C++运作
- 为什么我们应该在使用"using namespace std"时使用"#include<iostream>"?
- 如何在代码中使用 STD 而不包含"using namespace std"?
- 如何正确使用"using namespace foo?"
- 使用不带前缀"std"且不带"using namespace std;"的 std::sort() 成功编译
- 总是"using namespace std" ?
- 头文件中可以包含"using namespace"语句吗?
- 为什么以及如何"using namespace"声明会混淆C++中的编译器?
- "using namespace std;"是否免于过多代码规则?
- C++如何知道在哪里查找使用 "using namespace ..." 指定的命名空间?