Suvarna Pattayil
Suvarna Pattayil

Reputation: 5239

Namespaces confusion

I am new to namespaces and was trying this from C++ Primer

#include<iostream>
namespace Jill 
{
 double bucket;
 double fetch;
 struct Hill{ };
}

double fetch;

int main()
{
 using namespace Jill;
 Hill Thrill;
 double water = bucket; 
 //double fetch; //<<<<<<<<<<<<//
 std::cin>> fetch;
 std::cin>> ::fetch;
 std::cin>> Jill::fetch;
 std::cout<<"fetch is "<<fetch;
 std::cout<<"::fetch is "<< ::fetch;
 std::cout<<"Jill::fetch is "<< Jill::fetch;
}

int foom()
{
 Jill::Hill top;
 Jill::Hill crest;
}

When the line marked //<<<<<<<<<<<<// is not commented I get expected results. i.e. the local variable hides the global and Jill::fetch. But when I comment it out, there are 2 fetch left . global fetch and Jill::fetch. And the compiler gives the error

namespaceTrial1.cpp:17:13: error: reference to ‘fetch’ is ambiguous
namespaceTrial1.cpp:9:8: error: candidates are: double fetch
namespaceTrial1.cpp:5:9: error:                 double Jill::fetch
namespaceTrial1.cpp:20:26: error: reference to ‘fetch’ is ambiguous
namespaceTrial1.cpp:9:8: error: candidates are: double fetch
namespaceTrial1.cpp:5:9: error:                 double Jill::fetch

My question is why does the compiler get confused this lead to ambiguity? Why does it not assume fetch as just Jill::fetch , since I have added using namespace Jill at the start of main()

If I use declarative using Jill::fetch; at the start of main, the issue gets solved. because using Jill::fetch makes it as if it has been declared at that location. So, its like there is a local fetch variable. [Am i correct?] Why does using declaration behave as if the variable was declared at that location and using directive doesnt?

Upvotes: 5

Views: 810

Answers (4)

Sebastian Redl
Sebastian Redl

Reputation: 71979

The using directive modifies name lookup in a way that isn't exactly intuitive to most programmers. The standard says this in [namespace.udir]p2:

During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

This wording means that names from the namespace do not appear in the current scope, but in some outer scope. In your example the using directive is in the function which is in the global namespace, and the Jill is also in the global namespace, so the names from Jill appears as if they are in the global namespace. (As Joachim said, the names aren't actually introduced there, so they don't immediately conflict with existing names and you only get ambiguous lookups when you actually use them.)

This is a simple case and the compiler gives you an error, which is good. It can actually get more complicated than that.

namespace Outer {
  namespace Mid1 { int i = 1; }
  namespace Mid2 {
    namespace Tricky {
      int i = 2;
      namespace Inner {
        void f() {
          using namespace Mid1;
          std::cout << i;
        }
      }
    }
  }
}

This will output 2, not 1, even though you had the using directive right next to the line referring to i. But the nearest enclosing namespace that contains both Mid1 and the using directive is Outer, so Mid1::i acts as if it was declared in Outer. If you had an Outer::i it would be shadowed by Tricky::i, and Mid1::i fares no better.

An easy solution is to ban using directives and only use using declarations and namespace aliases. They're far more intuitive.

Upvotes: 3

NonNumeric
NonNumeric

Reputation: 1109

using namespace will not give priority to names it imports resulting in what you already observed.

Getting ambiguity error here is language design decision: it would be rather dangerous for such prioritization to exist. Imagine that the Jill namespace is a large one maintained by several developers probably from different organization. You have no or limited control to its contents and still changes to it might silently change the meaning of your program.

Upvotes: 3

BЈовић
BЈовић

Reputation: 64223

[basic.scope.declaration] says that a local variable hides the global, after it's declaration

int j = 24;
int main() {
int i = j, j;
j = 42;
}

the identifier j is declared twice as a name (and used twice). The declarative region of the first j includes the entire example. The potential scope of the first j begins immediately after that j and extends to the end of the program, but its (actual) scope excludes the text between the , and the }. The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }, but its potential scope excludes the declaration of i. The scope of the second declaration of j is the same as its potential scope.

This explains why there isn't an error when you declare a local variable.

When the local variable is not declared, then you have a name clash. The compiler can not decide which fetch to pick, and throws an error.

Upvotes: 2

Some programmer dude
Some programmer dude

Reputation: 409176

When you declare a local variable shadowing a global/namespace variable, you explicitly tell the compiler about that. However, when you are using using the variables of the namespace doesn't actually end up in the local scope.

From the specification (section 7.3.4, point 3):

A using-directive does not add any members to the declarative region in which it appears.

Also (from same section, point 6):

If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.

Upvotes: 4

Related Questions