What does "using namespace" do exactly?
Asked Answered
M

3

21

The following C++ test code does not link (gcc 4.9.2, binutils 2.25). The error is In function 'main': undefined reference to 'X::test'.

01: #include <string>
02: #include <iostream>
03:
04: namespace X
05: {
06:     extern std::string test;
07: };
08:
09: using namespace X;
10: std::string test = "Test";
11:
12: int main()
13: {
14:    std::cout << X::test << std::endl;
15: }

Because of line 09, I was expecting line 10 to define the X::test variable declared on line 06. I believe that instead an unrelated test variable is declared and defined in the global namespace, hence the linking error.

Question: Could anyone please explain why my expectation was incorrect, and what is happening exactly?

Not the answer:

  • I can make it link changing line 10 to std::string X::test = "Test";.
  • I should not use "using namespace" to begin with.
Misericord answered 7/12, 2015 at 8:21 Comment(7)
Since you say inside the namespace extern it looks for it ouside the namespace. It does not get defined inside the namespace and the compiler can't find it, Undefined reference. Question to you: Why does it need to be extern?Bucket
@wouter140: extern has nothing to do with "looking for something outside the namespace". It just means "this is defined elsewhere (extern)al".Tailrace
@curiousguy Literally "somewhere else," or actually anywhere else. It just means "this is just a declaration, not a definition."Recipe
Maybe you could post code without line numbers, or with number in comments. Not everyone uses a text editor that allows rectangular selection (like vim and emacs do).Hypotaxis
@Hypotaxis And like Notepad++ does (to name a non-primarily-Unix-world editor too).Recipe
@curiousguy: I understand I'm asking a question and hoping for kind help from the community. However shouldn't developers not using the right tool for the job really be their problem rather than mine? Following examples/tutos online very often does require an editor with such a feature. This said I would love to make my question better: would you have a link to better formatted code sample in SO?Misericord
@Bucket and those wondering: In real life the namespace definition is in a header included in multiple source files. This way each translation unit knows about the variable declaration, but the definition still happens just once and the linker can do its job. Without the extern the translation units with only the declaration would fail to compile.Misericord
R
30

The directive using namespace X; makes names from namespace X visible inside the namespace containing the directive. That is, when looking up a name n in that scope, X::n can be found. However, it will only be looked for if the compiler needs to look for it.

In your example, this declaration:

std::string test = "Test";

inside the global namespace makes perfect sense as-is. The name test is simply introduced, as with any other declaration. No need to look it up anywhere.

This would be an entirely different kettle of fish:

namespace X
{
  struct C
  {
    static std::string test;
  };
}

using namespace X;
std::string C::test = "Test";

In this code, the compiler needs to know what C is to make sense of the definition of C::test. It therefore does a name lookup of C, which indeed finds X::C thanks to the using directive.

Recipe answered 7/12, 2015 at 8:28 Comment(1)
Spot on answer. The scenario with the struct is very interesting too. Thanks.Misericord
C
9

using namespace means you use definitions from the namespace you specified, but it doesn't mean that everything that you define is being defined in a namespace you use.

Logic of this behavior is pretty simple. Let's say we have the following example:

namespace X
{
    extern string test;
};

namespace Y
{
    extern string test;
};

using namespace X;
using namespace Y;

string test = "value";

Following your example logic, compiler just would not know in which namespace it should define test, so you would have to declare the namespace explicitly. In a real life it is defined in a global namespace.

In your specific case you define the test variable outside of the X namespace where it is declared as extern. Linker looks for definition of the X::test but doesn't find and as a result you see this error.

Colorful answered 7/12, 2015 at 8:28 Comment(7)
I agree that in your code there is an ambiguity. However I do not feel you succeed in explaining why in my example the compiler decides to declare and define a new variable in the global namespace rather than find the existing one in the namespace I'm using.Misericord
@Misericord probably, my explanation is not the best one, but in this case if you want a variable in a global namespace, you would need define it as ::test and it is still explicit declaration. In general, compiler does what you say and should not guess what you wanted.Colorful
@marcv81: The point is that the compiler does not "search" for a declaration when defining an identifier, at all. (An identifier does not need to be declared prior to being defined.) The statement std::string test = ... defines a variable test in the current namespace, which using namespace ... does not affect; it only affects which namespaces are searched for identifiers. An identifier is only "searched" when it is referred to -- which a definition does not. Which is pretty exactly what Alex was trying to say.Tailrace
@DevSolar: what I really wanted was to write using namespace X; once then never write X:: ever again. I suppose the issue is that in C++ we cannot define a variable without implicitly declaring it too rather than referring to an existing declaration. And as you point out if it can be declared without falling back to a declaration from the namespace it will.Misericord
@marcv81: The better style is the other way around, always writing X:: and never writing using namespace X;. The latter is a crutch, really, because it takes away information from the source. (You cannot tell which namespace a given identifier stems from, which is a royal PITA when debugging source that went "using namespace" on half a dozen of them. If the namespace name is too long to type repeatedly, use namespace short = very::long::namespace; and short::identifier.Tailrace
A pattern I like is namespace vln = very::long::namespace;.Francie
@DevSolar: never writing using namespace is a good rule, but always writing X:: is not. Qualified names break ADL. It is far better in some cases to apply a using declaration in local scope to introduce individual names from the namespace into the overload set, but let overload resolution choose whether those are selected in the end.Quidnunc
B
3

Here is a declaration of variable test in namespace X.

04: namespace X
05: {
06:     extern std::string test;
07: };

It is not a definition of the variable. The variable must be also defined before it can be used to get its value.

You could make this declaration also a definition if you initialize the variable. For example

04: namespace X
05: {
06:     extern std::string test = "Test";
07: };

In this case the code would compile successfully.

In this statement

14:    std::cout << X::test << std::endl;

there is an access to the qualified name X::test. The compiler searches this name in namespace X as it is specified in the variable and finds the declaration. Now it needs to get the value of the variable but it is unable to find its definition.

In this statement

10: std::string test = "Test";

there is declared and defined variable test in the global namespace because it is declared outside any explicitly specified namespace.

You could write

10: std::string X::test = "Test";
                ^^^^^^^

instead of

10: std::string test = "Test";

if you want to define the variable declared in the namespace X.

As for the using directive then it introduces names declared in the specified namespace in the namespace where the directive is used.

For example If to write using unqualified name test

14:    std::cout << test << std::endl;
                    ^^^^^

then there will be an ambiguity because this name can refer to name X::test and ::test due to the using directive.

Bonus answered 7/12, 2015 at 8:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.