关于在执行非限定调用时使用名称与使用命名空间的歧义



我知道这会模棱两可

#include <boost/hana/fwd/equal.hpp>
#include <range/v3/algorithm/equal.hpp>
#include <vector>
int main() {
std::vector<int> v{1,2,3};
using namespace boost::hana;
using namespace ranges;
equal(v, v);
}

因为在boost::hanaranges命名空间中都有equal

但是,我认为这也将是模棱两可的:

#include <boost/hana/fwd/equal.hpp>
#include <range/v3/algorithm/equal.hpp>
#include <vector>
int main() {
std::vector<int> v{1,2,3};
using namespace boost::hana;
using ranges::equal;
equal(v, v);
}

但根据GCC和Clang的说法,情况并非如此。

为什么?

片段 2

让我们看看示例中的第二个代码段是如何工作的,因为您已经知道第一个代码段产生不明确错误的原因。

从使用指令的文档:

仅在命名空间范围和块范围内允许使用 using 指令。从 using-指令之后的任何名称的非限定名称查找的角度来看,直到它出现的范围的末尾,命名空间名称中的每个名称都是可见的,就好像它是在最近的封闭命名空间中声明的,该命名空间同时包含 using 指令和命名空间名称。

using-指令不会向它所在的声明性区域添加任何名称(与 using-声明不同),因此不会阻止声明相同的名称。

这意味着using directive不会在声明性区域中引入 name(在示例中只不过是main函数),而是引入最近的封闭命名空间(这是示例中的global namespace)。

现在,当调用表达式发生非限定查找equal(v, v);搜索从遇到调用表达式的点向上,并找到由于声明性区域中using declaration而引入的equal版本(即main),因此搜索停止。因此,使用了这个已经找到的版本。

一个人为的例子可能有助于澄清情况:

#include <iostream>
namespace X 
{
void func(){std::cout<<"X version called"<<std::endl;}
}
namespace Y 
{
void func(){std::cout<<"Y version called"<<std::endl;};
}
int main()
{
using namespace X;
using Y::func;

func(); //calls Y version
return 0;
}

演示

<小时 />

片段 1

示例中的代码段 1 也可以根据上面引用的语句来理解。特别是,代码段 1 中出现不明确的错误,因为命名空间rangesboost::hana都有一个名为equal的函数,这些函数排名相同。因此,当调用表达式equal(v,v)限定名称查找发生时,搜索将从遇到调用表达式的位置开始并向上,并查找名为equal的函数,这些函数由于两个命名空间而在全局名称范围内可见。此外,由于两者的排名相同,我们得到了提到的模棱两可的错误。

一个人为的例子可能有助于澄清情况:

#include <iostream>
namespace X 
{
void func(int)
{
std::cout<<"X version func called"<<std::endl;
}

void foo()
{
std::cout<<"X version foo called"<<std::endl;
}
}
namespace Y 
{
void func(double)
{
std::cout<<"Y version func called"<<std::endl;
}
void foo()
{
std::cout<<"Y version foo called"<<std::endl;
}
}
int main()
{
using namespace X ;
using namespace Y;

func(3); //calls X's func 
func(5.5);//calls Y's func

foo();//fails due to ambiguity
return 0;
}

演示