命名空间对 C++ 模板推导优先级的影响

Impact of namespaces on C++ template deduction priority

提问人:J.M 提问时间:1/14/2022 最后编辑:Vlad from MoscowJ.M 更新时间:1/14/2022 访问量:113

问:

在尝试实现一个元函数时,只有当某种类型的“abs”函数存在时,它才需要存在,我遇到了以下问题:

以下是两个代码示例,我希望产生相同的结果,但实际上,它们没有:

第一个例子

#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();
} 

收益 率:

2
-1

这就是我所期望的

第二个例子

#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();
} 

收益 率:

-1
-1

为什么在这里使用命名空间会让编译器优先考虑“本地”abs 方法而不是 std::abs?

C++ 模板 命名空间 name-lookup using 指令

评论


答:

3赞 Vlad from Moscow 1/14/2022 #1

在第二种情况下,using 指令将声明的名称放在全局命名空间的指定命名空间中,以进行非限定名称查找。因此,在命名空间中,可以找到在此命名空间中声明的非限定名称。也就是说,在命名空间中声明的名称隐藏了在全局命名空间中声明的名称。FooabsabsFooabs

来自 C++ 14 标准(7.3.4 Using 指令)

2 using 指令指定 namespace 可用于 using 指令的作用域 出现在 using 指令之后。在非限定名称查找期间 (3.4.1),名称看起来好像是用最近的 enclosed 命名空间,其中包含 using 指令和 Nominated 命名空间。[注:在此上下文中,“包含”是指 “直接或间接包含”。——尾注 ]

第二个程序中最接近的封闭命名空间是全局命名空间。

下面还有两个程序,它们演示了在使用 using 指令时进行非限定名称查找的相同原理。

#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 function s() called.

在此演示程序中,函数的声明隐藏了结构的声明。ss

第二个程序。

#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 constructor of struct N2::s called.

在这种情况下,具有名称的结构的声明隐藏了具有相同名称的函数,该函数的声明由于 using 指令而被放置在全局命名空间中。ss

1赞 Karen Baghdasaryan 1/14/2022 #2

至于发生这种情况的原因,你可以参考弗拉德的回答。但是,如果仍希望能够执行测试,则可以在单独的命名空间中执行测试。

#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();
} 

输出为

2
-1

评论

1赞 J.M 1/14/2022
谢谢,这是对弗拉德回答的巧妙补充。