我最近在一个 C++ 项目的源文件中看到了这段代码:
using namespace std;
#include <iostream>
忽略所有是否是一个好主意的问题,using namespace std
上面的代码是否合法?这两行之前的文件中没有代码。
我原以为这不会编译,因为namespace std
在指令将其包含到文件中之前尚未在范围内声明#include <iostream>
,但是使用项目的构建系统编译就好了。如果有人有指向规范相关部分的链接,那将不胜感激。
我最近在一个 C++ 项目的源文件中看到了这段代码:
using namespace std;
#include <iostream>
忽略所有是否是一个好主意的问题,using namespace std
上面的代码是否合法?这两行之前的文件中没有代码。
我原以为这不会编译,因为namespace std
在指令将其包含到文件中之前尚未在范围内声明#include <iostream>
,但是使用项目的构建系统编译就好了。如果有人有指向规范相关部分的链接,那将不胜感激。
一个可能有趣的数据点。当我编译以下内容时:
using namespace std;
using namespace no_such_namespace;
使用 g++ 4.5.2,我得到:
c.cpp:2:17: error: ‘no_such_namespace’ is not a namespace-name
c.cpp:2:34: error: expected namespace-name before ‘;’ token
需要明确的是,这两行是我编译的整个源文件。
那时既std
没有也没有no_such_namespace
被定义为命名空间,但 g++ 只抱怨第二个。在没有声明的情况下,我认为标识符没有什么特别之处。std
我认为@James Kanze 是正确的,这是 g++ 中的一个错误。
编辑:据报道。(5年前!)
更新:现在已经 8 年多了,仍然没有分配给任何人,更不用说固定了。g++ 4.9.2 出现了这个问题。clang++ 3.5 没有,但它会发出警告std
和致命错误no_such_namespace
:
c.cpp:1:17: warning: using directive refers to implicitly-defined namespace 'std'
using namespace std;
^
c.cpp:2:17: error: expected namespace name
using namespace no_such_namespace;
^
1 warning and 1 error generated.
更新:截至 2021 年 9 月 24 日,错误报告仍处于打开状态,并且该错误存在于 g++ 11.2.0 中。2021-07-24 发布的评论表明 g++ 应该对此发出警告。
我认为这不合法,但标准并不是 100% 清楚。基本上,名称查找(如 §3.4 中所定义)找不到名称空间的先前声明,因为没有。一切都取决于是否:
using namespace std;
是否是命名空间的声明。而且我在第 7.3.4 节中没有看到任何说明 using-directive 声明了指定命名空间的文本。G++ 允许您的代码,但恕我直言,这是一个错误。
来自 SO/IEC 14882:2003
[7.3.3.9] 由 using-declaration 声明的实体应根据 using-declaration 处的定义在使用它的上下文中为人所知。在使用该名称时,不考虑在 using 声明之后添加到命名空间的定义。
[3.4.3.2.2] 给定 X::m(其中 X 是用户声明的命名空间),或给定 ::m(其中 X 是全局命名空间),令 S 是 X 中 m 的所有声明的集合,并且在 X 及其使用的命名空间中由 using 指令指定的所有命名空间的传递闭包中,除了在直接包含一个或多个 m 声明的任何命名空间(包括 X)中忽略 using 指令。在名称查找中不会多次搜索命名空间。如果 S 是空集,则程序是非良构的。否则,如果 S 正好有一个成员,或者如果引用的上下文是 using-declaration (7.3.3),则 S 是 m 的必需声明集。否则,如果 m 的使用不允许从 S 中选择唯一声明,则程序是非良构的
因此,如果它碰巧起作用,那只是侥幸而不是便携式的。
此代码是未定义的行为 [lib.using.headers]:
翻译单元应仅在任何外部声明或定义之外包含标头,并且应在对其在该翻译单元中声明或首先定义的任何实体的第一次引用之前在词法上包含标头。
您引用std
然后包含一个声明它的标题。即使这仍然是未定义的行为:
#include <string>
using namespace std;
#include <iostream>
我认为关于这种情况,标准(包括 C++0x)存在缺陷。
我们在第 3.3.6 节 ( [basic.scope.namespace]
) 中有:
命名空间定义的声明区域是它的命名空间主体。由 original-namespace-name 表示的潜在范围是由同一声明区域中的每个命名空间定义与该 original-namespace-name 建立的声明区域的串联。在命名空间主体中声明的实体被称为命名空间的成员,由这些声明引入命名空间的声明区域的名称被称为命名空间的成员名称。命名空间成员名称具有命名空间范围。它的潜在范围包括从名称声明点(3.3.2)开始的名称空间;对于每个指定成员命名空间的 using 指令 (7.3.4),
和
翻译单元的最外层声明区域也是一个命名空间,称为全局命名空间。在全局命名空间中声明的名称具有全局命名空间范围(也称为全局范围)。这种名称的潜在范围从其声明点 (3.3.2) 开始,并在作为其声明区域的翻译单元的末尾结束。具有全局命名空间范围的名称被称为全局名称。
namespace std
全局命名空间的成员也是如此,名称的范围从声明点开始。
而 3.3.2 ( [basic.scope.pdecl]
) 告诉我们:
名称的声明点紧跟在它的完整声明符(第 8 条)之后和它的初始化器(如果有的话)之前,除非下面提到。
并且没有任何例外适用于命名空间。
因此命名空间名称不能在其声明符之前使用,但命名空间名称不是声明符。哎呀。
最近我遇到了同样的问题,我的技术负责人建议我这样做;using namespace 不保证方法的可见性,直到具有相关方法的命名空间包含在文件 using .h 文件中。包括头文件解决了这个问题。