Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Namespaces confusion

Tags:

c++

namespaces

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?

like image 957
Suvarna Pattayil Avatar asked Apr 18 '13 11:04

Suvarna Pattayil


People also ask

What is namespace confusion?

A Namespace Confusion attack is a type of software supply chain attack that tricks a package manager into downloading a malicious component instead of a proprietary one. To do this, bad actors upload a component with the same name as a proprietary component to an ecosystem with no namespace.

What is a nexus namespace?

Nexus has a multi-tiered naming system that offers globally unique identification, personalization and branding opportunities. Three categories exist: local names, namespaces, and global names, all of which can be created via the Nexus desktop wallet.


2 Answers

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.

like image 79
Some programmer dude Avatar answered Sep 21 '22 21:09

Some programmer dude


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.

like image 22
Sebastian Redl Avatar answered Sep 18 '22 21:09

Sebastian Redl