简体   繁体   中英

Impact of namespaces on C++ template deduction priority

while trying to implement a metafunction, which needs to exist only if the "abs" function exists for some type, i ran into the folowing problem:

Here are two examples of code i would expect to yield the same results but in fact, they do not:

First example

#include <iostream>
#include <cmath>

using namespace std;

struct Bar{};

template<typename T>
int abs(T& v)
{
   return -1;
}

void test()
{
   Bar b;
   double x = -2;
   cout << abs(x) << endl;
   cout << abs(b) << endl;
}

int main()
{
    test();
} 

Yields:

2
-1

Which is what i expect

Second example

#include <iostream>
#include <cmath>

namespace Foo
{
   struct Bar{};

   using namespace std;
    
   template<typename T>
   int abs(T& v)
   {
       return -1;
   }

   void test()
   {
    Bar b;
    double x = -2;
    cout << abs(x) << endl;
    cout << abs(b) << endl;
   }
}

int main()
{
    Foo::test();
} 

Yields:

-1
-1

Why using a namespace here is making the compiler prioritize the "local" abs methodthe over std::abs?

In the second case the using directive places declared names in the nominated namespace in the global namespace for unqualified name lookup. So within the namespace Foo the unqualified name abs declared in this namespace is found. That is the name abs declared in the namespace Foo hides the name abs declared in the global namespace.

From the C++ 14 Standard (7.3.4 Using directive)

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 ]

The nearest enclosing namespace in the second program is the global namespace.

Below there are two more programs that demonstrate the same principle of the unqualified name lookup when a using directive is used.

#include <iostream>

void s() { std::cout << "The function s() called.\n"; }

struct s
{
    s() { std::cout << "The constructor of struct s called.\n"; }
};

void test()
{
    s();
}

int main()
{
    test();
}

The program output is

The function s() called.

In this demonstration program the declaration of the function s hides the declaration of the structure s .

Second program.

#include <iostream>

namespace N1
{
    void s() { std::cout << "The function N1::s() called.\n"; }
}

namespace N2
{
    using namespace N1;

    struct s
    {
        s() { std::cout << "The constructor of struct N2::s called.\n"; }
    };

    void test()
    {
        s();
    }
}

int main()
{
    N2::test();
}

The program output is

The constructor of struct N2::s called.

In this case the declaration of the structure with the name s hides the function with the same name s the declaration of which was placed in the global namespace due to the using directive.

For the reason why this takes place you can refer to Vlad's answer. However, if you want to still be able to perform the testing, you can do the testing in separate namespace.

#include <iostream>
#include <cmath>

namespace Foo
{   
   template<typename T>
   int abs(T& v)
   {
       return -1;
   }
}

namespace Test
{
    struct Bar{};
    using namespace std;
    using namespace Foo;
    void test()
    {
       Bar b;
       double x = -2;
       cout << abs(x) << endl;
       cout << abs(b) << endl;
    }
}

int main()
{
    Test::test();
} 

The output is

2
-1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM