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.
curiousguy
  • 8,038
  • 2
  • 40
  • 58
marcv81
  • 850
  • 8
  • 22
  • 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? – wouter140 Dec 07 '15 at 08:42
  • 3
    @wouter140: `extern` has nothing to do with "looking for something outside the namespace". It just means "this is defined elsewhere (extern)al". – DevSolar Dec 07 '15 at 08:58
  • 1
    @curiousguy Literally "somewhere else," or actually anywhere else. It just means "this is just a declaration, not a definition." – Angew is no longer proud of SO Dec 07 '15 at 09:30
  • 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). – curiousguy Dec 07 '15 at 09:32
  • 1
    @curiousguy And like Notepad++ does (to name a non-primarily-Unix-world editor too). – Angew is no longer proud of SO Dec 07 '15 at 09:33
  • @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? – marcv81 Dec 07 '15 at 09:40
  • @wouter140 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. – marcv81 Dec 07 '15 at 09:49

3 Answers3

29

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.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
8

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.

Alex
  • 9,891
  • 11
  • 53
  • 87
  • 1
    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. – marcv81 Dec 07 '15 at 09:59
  • @marcv81 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. – Alex Dec 07 '15 at 10:17
  • 2
    @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. – DevSolar Dec 07 '15 at 10:25
  • @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. – marcv81 Dec 07 '15 at 10:31
  • 3
    @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`. – DevSolar Dec 07 '15 at 10:50
  • A pattern I like is `namespace vln = very::long::namespace;`. – OrangeDog Dec 07 '15 at 14:24
  • 1
    @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. – Ben Voigt Dec 07 '15 at 15:31
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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335