Ordering of using namespace std; and includes?
I recently saw this code being used in a source file in a C++ project:
using namespace std;
#include <iostream>
Ignoring all issues of whether it's a good idea to have using namespace std
at all, is the above code even legal? There is no code in the file before these two lines.
I would have thought that this wouldn't compile, since namespace std
hasn't been declared in scope until the #include <iostream>
directive includes it into the file, but using the build system for the project this was compiling just fi开发者_运维知识库ne. If someone has a link to a relevant part of the spec, that would be most appreciated.
A perhaps interesting data point. When I compile the following:
using namespace std;
using namespace no_such_namespace;
with g++ 4.5.2, I get:
c.cpp:2:17: error: ‘no_such_namespace’ is not a namespace-name
c.cpp:2:34: error: expected namespace-name before ‘;’ token
To be clear, those two lines are the entire source file I compiled.
Neither std
nor no_such_namespace
has been defined as a namespace at that point, but g++ complains only about the second. I don't think there's anything special about the identifier std
in the absence of a declaration of it. I think @James Kanze is right that this is a bug in g++.
EDIT: And it's been reported. (5 years ago!)
UPDATE: Now it's more than 8 years, and still hasn't been assigned to anyone, much less fixed. g++ 4.9.2 exhibits the problem. clang++ 3.5 doesn't, but it issues a warning for std
and a fatal error for 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.
UPDATE: As of 2021-09-24, the bug report is still open and the bug exists in g++ 11.2.0. A comment posted 2021-07-24 suggests that g++ should warn about this.
I don't think it's legal, but the standard isn't 100% clear about it. Basically, name lookup (as defined in §3.4) can't find a previous declaration of the namespace, because there isn't one. Everything hinges on whether:
using namespace std;
is a declaration of the namespace or not. And I don't see any text in §7.3.4 which says that a using-directive declares the nominated namespace. G++ allows your code, but IMHO, this is a bug.
From SO/IEC 14882:2003
[7.3.3.9] The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration. Definitions added to the namespace after the using-declaration are not considered when a use of the name is made.
[3.4.3.2.2] Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), let S be the set of all declarations of m in X and in the transitive closure of all namespaces nominated by using-directives in X and its used namespaces, except that using-directives are ignored in any namespace, including X, directly containing one or more declarations of m. No namespace is searched more than once in the lookup of a name. If S is the empty set, the program is ill-formed. Otherwise, if S has exactly one member, or if the context of the reference is a using-declaration (7.3.3), S is the required set of declarations of m. Otherwise if the use of m is not one that allows a unique declaration to be chosen from S, the program is ill-formed
So if it happens to work, it's a fluke and not portable.
This code is undefined behavior [lib.using.headers]:
A translation unit shall include a header only outside of any external declaration or definition, and shall include the header lexically before the first reference to any of the entities it declares or first defines in that translation unit.
You reference std
and then include a header that declares it. Even this is still undefined behavior:
#include <string>
using namespace std;
#include <iostream>
I think there's a flaw in the standard (including C++0x) with respect to this case.
We have in section 3.3.6 ([basic.scope.namespace]
):
The declarative region of a namespace-definition is its namespace-body. The potential scope denoted by an original-namespace-name is the concatenation of the declarative regions established by each of the namespace-definitions in the same declarative region with that original-namespace-name. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration (3.3.2) onwards; and for each using-directive (7.3.4) that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration.
and
The outermost declarative region of a translation unit is also a namespace, called the global namespace. A name declared in the global namespace has global namespace scope (also called global scope). The potential scope of such a name begins at its point of declaration (3.3.2) and ends at the end of the translation unit that is its declarative region. Names with global namespace scope are said to be global name.
So namespace std
is a member of the global namespace, and the scope of the name starts at the point of declaration.
And 3.3.2 ([basic.scope.pdecl]
) tells us:
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below.
And none of the exceptions apply to namespaces.
So a namespace name cannot be used before its declarator, but a namespace name isn't a declarator. Oops.
Recently i faced the same issue and been advised by my tech lead that; using namespace does not guarantee the visibility of the methods until the namespace with related methods are included in the file using .h file. including the header file resolved the issue.
精彩评论