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

About the ambiguity of using a name vs using a namespace when doing unqualified calls

提问人:Enlico 提问时间:3/31/2022 最后编辑:Enlico 更新时间:4/7/2022 访问量:243

问:

我知道这会是模棱两可的

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

因为有一个 in 和 namespaces。equalboost::hanaranges

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

#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 的说法,情况并非如此。

为什么?

C++ 命名空间 C++17 language-lawyer using 指令

评论

1赞 StoryTeller - Unslander Monica 3/31/2022
stackoverflow.com/q/48356856/817643
1赞 Language Lawyer 3/31/2022
quuxplusone.github.io/blog/2020/12/21/using-directive
1赞 Language Lawyer 3/31/2022
与大多数声明一样,当搜索包含声明的作用域时,只需通过查找即可找到 using-declarationsusing 指令“工作”以截然不同的方式。这样的代码示例对于理解这种复杂的措辞的含义非常有用。
0赞 Language Lawyer 4/1/2022
不过,不确定 dup 目标是否相关。这个问题(和答案)是关于合格的查找,而这是关于不合格的。
0赞 Enlico 4/1/2022
@LanguageLawyer,哦,这是真的!我误读了:(

答:

2赞 user12002570 4/1/2022 #1

片段 2

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

Using 指令的文档

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

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

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

现在,当对调用表达式进行非限定查找时,搜索会从遇到调用表达式的点向上移动,并找到由于声明性区域(即 )而引入的版本,因此搜索停止。所以这个已经找到的版本被使用了。equal(v, v);equalusing declarationmain

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

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

演示