Unqualified function call selects function from a wrong namespace

Viewed 279

Consider this code:

#include <iostream>

namespace A {
    struct Mine {};

    template <typename T1, typename T2>
    void foo(T1, T2)
    {
        std::cout << "A::foo" << std::endl;
    }
}

namespace B {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}   

using namespace A;
using namespace B;
// or with the same effect:
//using A::foo;
//using B::foo;

int main()
{
    A::Mine a;
    foo(a, a);
}

The program prints B::foo instead of A::foo. Why does it use B::foo instead of A::foo?

Imagine the following situation: your library provides a namespace A and some other library provides namespace B which includes a function template with the same name foo as your library. Normally, there is no problem since the correct version can be selected using qualified calls or relying on the argument-dependent lookup. But if the user introduces both A::foo and B::foo to the same scope using a using declaration, the unqualified call is not ambiguous but the wrong function may be selected.

Is there a way to prefer A::foo over B::foo, since according to the argument-dependent lookup A::foo should be selected? What would be your advice in this situation?

1 Answers

It's actually doing the correct thing. Here's the reason.

namespace A {
    struct Mine {};

    template <typename T1, typename T2>
    void foo(T1, T2)
    {
        std::cout << "A::foo" << std::endl;
    }
}

The above code will be matched when there are 2 parameters passed of different (or same) type. Because you've defined both T1 and T2 differently (but they can also be same). But the function call is

foo(a, a);

having both the parameters of same type. Now a method foo with 2 parameters of same type is defined in the namespace B as below

namespace B {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}

And thus the method from namespace B is matched, as both the signatures are different.

Try adding the below code

namespace C {
    template <typename T>
    void foo(T, T)
    {
        std::cout << "B::foo" << std::endl;
    }
}

and you'll end up with a compiler error, specifying there's ambiguity in the method definition and then you would have to manually resolve the scope of foo using something like A::foo(a,a) or whatever namespace you wish to use.

Related