c++ ADL(Argument-Dependent Lookup)查找
发布日期:2021-06-29 16:00:36 浏览次数:2 分类:技术文章

本文共 2448 字,大约阅读时间需要 8 分钟。

0X00 简述

ADL全称是Argument-Dependent Lookup的简写,作用是扩展命名空间的查找范围,通过函数参数查找函数的命名空间。

0x01 问题引出

我们通过一个例子引出为什么要有这种机制。现在我们有这样一种命名空间f1

namespace f1{    struct data {};    data operator+(const data& A, const data& B)    {        data C;        add(A, B, C);//A+B-->C        return C;    }}

我们想要实现这样的操作

A = B + C + D

如果没有ADL机制的话,我们式子就会变得很复杂

A = f1::operator+(f1::operator+(B, C), D);

而由于出现了ADL这种机制,我们的操作写法就变得很简洁

A = operator+(operator+(B, C), D);

这实际上等价于A = B + C + D;这种写法。我们这里通过ADL机制获取了参数的命名空间。

这个问题看上去很容易,但是一旦我们使用了多重命名空间、name hidingoverload混合,那么这个问题将会变得很复杂。

0x02 问题深入

我们看这样的一个问题

namespace c1{
namespace c2 {
struct cc{} void f(const cc& o){} } void f(const c2::cc& o){}}void f(const c1::c2::cc& o){}namespace f1{
void f(const c1::c2::cc& o){} namespace f2 {
void f(const c1::c2::cc& o){} void g() { c1::c2::cc o; f(o); } }}

这个时候我问你,在f1::f2::g中的f(o)使用的是哪个f ?我们分析一下,我们有几个选择

  • 因为我们知道参数o的类型是c1::c2::cc,所以我们通过ADL推测出命名空间为c1::c2,这时候的答案就是c1::c2::f
  • c1::f可以吗?
  • ::f可以吗?
  • f1::f可以吗?
  • f1::f2::f,这个有可能吗?可能啊,因为我们的g函数的命名空间就是f1::f2::g。同一个命名空间下的函数,这样调用也没问题啊。

OK! 这个时候问题就出现了。到底答案是哪个呢?我们加大难度,这个时候我们将全局函数f的参数去除const,变为这样

void f(c1::c2::cc& o){}

如果我们这个时候从函数重载(overload)的角度考虑这个问题,那么现在这个全局的f成为了最佳匹配。但是它被f1::f2::f这个函数给隐藏了(name hiding)。那么同样的道理f1::fc1::f也就不会被调用了。那么我们一定要使用这个全局函数怎么做呢?可以这样子做

void f(c1::c2::cc& o){}namespace f1{    void f(const c1::c2::cc& o){}    namespace f2    {        void f(const c1::c2::cc& o){}        using ::f;        void g()        {            c1::c2::cc o;            f(o);        }    }}

这个时候对于函数g,函数c1::c2::f::f都是可调用的,但是根据参数类型的最佳匹配原则,我们调用了::f

接着回到最初问题,这个时候我们就有了两个答案c1::c2::ff1::f2::f。实际上这个程序就会编译出错。

0x03 问题后续

我们接着看这样的问题

namespace c1{
namespace c2 {
struct cc{}; void f(cc& o){} //#1 }}void f(c1::c2::cc& o){}namespace f1{
namespace f2 {
void f(const c1::c2::cc& o){} //#2 void g() { c1::c2::cc o; const c1::c2::cc c(o); f(o); f(c); } void f(c1::c2::cc& o){} //#3 }}

因为#3是定义于g的后面,所以在g中是不可见的。全局函数::f#2隐藏(name hiding)。因此对于f(o)来说,我们通过使用ADL可以调用#1,我们通过name hiding也可以调用#2,但是我们最后调用最佳匹配#1。而对于f(c)我们通过同样的分析,我们知道调用#2

最后我们总结为以下三个步骤帮助我们判断

  • 找到所有的重载函数
    • 调用函数自己的命名空间中
    • 调用函数的父命名空间中
    • 参数的命名空间中
    • 通过using directive;导入的命名空间中(using namespace std;
    • 通过using declaration;导入的命名空间中(using std::cout;
  • 排除掉所有的name hiding
  • 选择最佳匹配(如果没有最佳匹配,程序编译出错)

转载地址:https://coordinate.blog.csdn.net/article/details/80007769 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:寻找数组中第n大的元素
下一篇:python fire使用指南

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月11日 10时53分11秒