Question

I don't understand why the following code gives error:

namespace  A
{
    void f(double x){cout<<"A::f(double)\n";}

    void f(string s){cout<<"A::f(string)\n";}

    namespace B
    {
        using namespace A;
        void f(int x){cout<<"B::f\n";}

        void call()
        {
            f(10);  // calls B::f, expected
            f(10.5); // calls B::f, why??
            string s="Hi";
            f(s);  // error, why??
        }
    }
}

My understanding was to search a name, compiler starts from current scope and continuously search enclosing scope and then global scope until it finds correct (correct means signature match or convertible in case of method) one, or gives error if none is present. So at first I tried without using namespace A in B. Then the first two call invoked B::f as expected, but f(s) was giving error. I thought names in enclosing scope are by default visible to inner scope, but clearly I was wrong. Then I place using namespace A and thought f(10.5) will call A::f for better type matching, and the problem with f(s) will be solved, but this is not the case (my understanding was using keyword bring everything to current scope from the scope which is being used). Can someone help me to understand how name lookup is applied here, thanks in advance.

Note I know how to make it work. I wanted to know why this is so? what C++ standard says about this in easy to understand language. Why my understanding is wrong?

Was it helpful?

Solution 3

That to understand the compiler behaviour it is important to reference to the following quote from the C++ Standard

2 A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. 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. [ Note: In this context, “contains” means “contains directly or indirectly”. —end note ]

In your example "the nearest enclosing namespace which contains both the using-directive and the nominated namespace." is namespace A. That is functions

void f(double x){cout<<"A::f(double)\n";}

void f(string s){cout<<"A::f(string)\n";}

are not members of namespace B for the purpose of the unqualified lookup.

Accotding to other quote from the C++ Standard

1 A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

these function declarations are hidden by the explicit declaration of f in namespace B. So the compiler found name f in namespace B and it stopped the further search of the name.

OTHER TIPS

Since, you are not calling A::f(), these will happen:

f(10.5); // cast to int
string s="Hi";
f(s);  //I do not know any B::f(), with string argument.

To be more exact, in the first line of the code I pasted, the compiler will try to call f(int a). You pass 10.5 and the function waits for an integer. However, 10.5 can be casted to an int, by cutting its decimal digits. This function is the one of namespace B.

[EDIT]

The default namespace is the one you have your functions inside. In order to not the use the default, you have to specify it yourself, be the :: operator.

If you remove the f() inside the namespace B, then the compiler will go to the namespace A, which is the 'outer' namespace.

Maybe this analogy with the scope of the variables can help.

int main() {

  int a = 10;

  {
    int a = 5;
    std::cout << a << std::endl;
  }

  return 0;
}

The output is 5. When it's time to print a, the compiler will go and find the last declared a, except otherwise told by us.

This is what is known as name hiding:

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

The declaration of B::f is hiding both overloads of A::f despite the using namespace declaration. The technical reason for which it wasn't working was because of the way unqualified lookup works. The compiler first searches the most local namespace for the symbol, and works its way up until it finds it. Implicit conversions are allowed which was why B::f was consistently being called and why the overload for std::string could not be chosen. To fix this, you can add the outer namespace function in explicitly using a using declaration:

using A::f;
void f(int) { ... }

void call()
{
    // ...
}

Here is a demo.

You are running into something called implicit conversion. Check out this page, or another one.

As pointed out in other answer, only the current scope is explored by the compiler, and he tries to convert the given argument to the one expected, and fails if he can't.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top