zoukankan      html  css  js  c++  java
  • C++ —— Argument Dependent Lookup

    *命名空间的出现对于C++的影响是非常大的,比如说using声明和using指令或者使用namespace作用域加以限定的名字。

    还记得自己阅读的第一份源码是Laurent Gomila写的SFML游戏引擎 的源代码,阅读的第一份源码居然如此优美实在感到非常幸运。不过其中对于namespace的使用也让当时青涩稚嫩的我大呼“卧槽屌炸了!”(注,这篇博客写于2015年3月,当年青涩稚嫩的我现在依然青涩单纯可爱……)

    命名空间也有很蛋疼的但是很有用的用途,ADL(Argument Dependent Lookup)就是其中之一。*

    namespace 名字空间

    回想起当年学Haskell的时候,看edx上的lecture,印象最深的一句话就是:Giving name is hard for programmers.

    不同的人可能给他自己写的类或函数中都有相同的名字,比方说如果有两个库,两个库中都有自定义的String类,那就往往会造成冲突。

    所以,namespace提供了解决这个问题的办法。举个例子,一个村有两个小明,一个住村口,一个住村里,那么我们为了区分两个小明,往往会使用“村口的小明”或“村里的小明”进行区分。那么“村口”以及“村里”就可以被认为是命名空间了。

    所以本质上,namespace是对全局作用域的细分。如下面的代码:

    namespace org_semantics { 
        class String (...) 
        String operator + ( const String &, const String &);
        ... 
    }

    这样一来就是将String放到命名空间org_semantics中,如果我们需要使用这个String,我们需要使用org_semantics::String来进行调用。

    在中山大学Sicily中混迹的我们都习惯于使用using namespace ...代码刷题,但是在项目中要记得尽量避免这类代码,这种代码风格不好。

    Argument Dependent Lookup(ADL)

    何为ADL?ADL的思想其实很简单:当编译器查找函数调用表达式的函数名字的时候,它会到所谓的“包含函数调用实参的类型”的名字空间中检查。

    考虑如下代码:

    namespace org_semantics{ 
        class X {...}; 
        void f (const X& ); 
        void g (X * ); 
        X operator + ( const X &, const X &); 
        class String {...}; 
    }
    int g ( org_semantics::X *);
    void aFunc() 
    { 
        org_semantics::X a ; 
        f(a ); // 调用 org_semantics::f 
        g (&a ); // 错误,歧义了 
    }

    在普通的函数查找中,是找不到org_semantics::f的,因为它被放在命名空间中。但是由于实参的类型是在命名空间中被定义的,因此编译器也会在那里去找候选的函数,这也就是f(a)能够成功调用的原因。

    当然,对于函数g来说,它的调用会发生错误。开发人员应该是想要调用全局的g函数,但是由于实参的类型是org_semantics ::X *,因此命名空间中的g函数也成为了候选函数。

    这往往会让开发者纠结,但是也未尝一定就是坏事——说不定命名空间中已经提供了这个功能的函数,编译器报错了提醒程序员有这个功能也说不定呢。

    值得注意的是,哪怕对g的调用导致了两个候选函数参与重载解析,全局作用于的g其实也没有重载org_semantics::g,因为它们不再同一个作用域。换句话说,ADL是关于函数是如何被调用的,而重载是关于函数如何被声明的,两者不在一个频道。

    最后的一句a = a + a,同样的,编译器会在命名空间中查找+操作符,所以ADL在重载操作符里面也发挥了作用。

    值得一提的是,很多程序员广泛使用了ADL却没有意识到这一点,比如下面这一段代码:

    org_semantics::String name ("金坷拉");
    std::cout << "Hello, " << name;

    在上面的代码中,第一个操作符<<很有可能调用的是std::basic_ostream的一个成员函数,而第二个则是位于命名空间的重载的operator <<的非成员函数的调用,这些细节对于这段代码的编写者来说可能注意不到,但是实际上ADL默默打理好了一切。

    <全文完>

  • 相关阅读:
    strcmp()比较函数和strcasecmp()和strnatcmp()
    substr()函数
    改变字符串中的字母大小写
    explode()与相反函数 implode() 和join()
    PHP nl2br() 函数
    PHP trim() 函数
    PHP的count(数组)和strlen(字符串)的内部实现
    变量处理函数库
    php中定义数组的方法
    80端口的烦恼:[3]清除NT Kernel占用80端口
  • 原文地址:https://www.cnblogs.com/arrowinmyknee/p/5470389.html
Copyright © 2011-2022 走看看