Ordering of using namespace std; and includes?
Asked Answered
H

6

11

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 fine. If someone has a link to a relevant part of the spec, that would be most appreciated.

Hogweed answered 27/7, 2011 at 7:55 Comment(3)
Was this code written in a source file, or a header file? Maybe it was included by another file that declared the namespace std.Epistaxis
This was in a source file, not a header file. That's a good point to clarify!Hogweed
clang++ gives a warning : using directive refers to implicitly-defined namespace 'std';Paucity
H
7

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.

Hyaluronidase answered 7/8, 2011 at 1:6 Comment(3)
Another update: g++ 9.2.0 still has the bug (which has not been assigned), though it no longer reports a syntax error.Hyaluronidase
Interesting data point. Perhaps C++ is including silently something, for example including #include<new> which includes std in turn?Severe
Thanks for updating this decade-old answer to give us a continued update on the saga!Hogweed
F
4

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.

Fann answered 27/7, 2011 at 9:45 Comment(0)
D
2

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.

Dripstone answered 27/7, 2011 at 8:7 Comment(2)
The highlighted part says that the statement should have no effect, but it does not explain whether it is legal.Epistaxis
Looking over this part of the spec, it seems like this refers to using declarations of the form using std::cout or using std::string, rather than something like using namespace std.Hogweed
J
1

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>
Julio answered 27/7, 2011 at 8:35 Comment(11)
Sorry, don't see how this applies. Which part specifically is violated? using namespace std; is not an enclosing external declaration or definition. It's not a reference to an entity declared or defined in the header, either.Besprent
@MSalters: It's a reference to an entity (namespace std) declared in the header. From [basic]: "An entity is a value, object, reference, function, enumerator, type, class member, template, template specialization, namespace, parameter pack, or this."Julio
@MSalters: this is good, because it's not so clear to me. Although my quote seems to prohibit OPs case, it also means that you cannot safely include standard library headers anywhere but in the outermost *.cpp. Note that C++11 FCD removed the 'first defines in that translation unit' part, which is even more suspicious.Julio
"Undefined behaviour" is a term for code which will compile and run but has unspecified results (this is not the same as unpredictable, though). If you don't #include things correctly then you are living in sin but the code might happen to be OK.Dripstone
@spraff: I'm sorry, you're wrong. As a counter-example, C++03 says: "If a source file that is not empty does not end in a new-line character, or ends in a new-line character immediately preceded by a backslash character, the behavior is undefined." This particular case is removed in C++0x but there are many other examples.Julio
@spraff: You're probably confusing with unspecified-behavior. See #2398484 for detailed explanation and relevant quatations.Julio
Ah, details details. No, I'm not confused about what "undefined behaviour" is, but it's a term that can only be applied to the results of compilation, the situation we're discussing is one which will not compile under all circumstances, or may compile to an undefined defined program, meaning that the contents of other headers may change the meaning of the program which is undefined before the fact, but given a particular set of other #includes we end up with a program that has no undefined behaviour. "undefined behaviour" != "Undefined Behaviour"Dripstone
@ybungalobill: Your interpretation can't be the intended meaning, since it precludes ever using more than one library header file in the same translation unit. You can't include <string> first, because it refers to namespace std, an entity declared in <iostream>. And vice versa.Vins
it is not using the global namespace entity "std". Since "std" was not declared, the compiler does not know what entity "std" refers to. It could be a namespace alias that could refer to a completely different namespace entity.Mizzen
In fact your example applies to any nontrivial program which has own source files and includes different headers.Mizzen
Ok the C++11 wording is different and does not make your example undefined anymore.Mizzen
V
1

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.

Vins answered 7/8, 2011 at 1:56 Comment(0)
A
0

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.

Ashworth answered 10/4, 2013 at 15:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.