《C++ primer》学习笔记之三十三:function overload resolution之一寻找候选函数集合

 
 1.寻找候选函数集合(Candidate Function Set):这一步的判断条件只有一个,即是否同名。
  函数调用肯定在另一个函数定义中:
   void caller()
   {
    T t;  //T为任意类型,可为内建类型
    f(t);  //找到函数f的定义
   }
  a)在caller中调用f前声明的同名函数:
   比如:
    namespace N
    {
     void f(char *);  //f in namespace N
    }
    
    namespace N2
    {
     void f(char *);  //f in namespace N
    }
   
    void caller()
    {
     using N::f;  //using function declare
     using namespace N2; //注意:using directive.这个与using declare不同,它不是函数声明,只是使namespace的成员在此处可见
          
     void f(double); //function declare
     
     T t;
     f(t);   //function call
     
     void f(int); //function declare, not a candidate
    }
   候选集合为{ N::f(char *), f(double) }, 没有N2::f(char *)
   这些函数会hidden在caller外面声明的同名函数,所以候选集合中就只这三个。
   如果在这一步找到了候选函数,则不会再依下面的步骤去找其它的候选函数。
  b)如果caller为类ClassType的成员函数,则到类ClassType中找同名的成员函数;如果没有找到,则到父类中找,找到了就不到父类中找。
   这些成员函数也会hidden在类ClassType外定义的同名函数。
   如果在这一步找到了候选函数,则不会再依下面的步骤去找其它的候选函数。
   
  注意:如果在a),b)中找到候选函数,则不会再去找其它候选函数。但下面的几步则不会,每步都会执行。
  c)在调用处可见的同名函数:
   namespace N
   {
    void f(char *);  //f in namespace N
   }
   
   void f(string);   //f in global scope
   using namespace N;  //名字空间N内的成员在global scope内可见
   void caller()
   {
    T t;
    f(t);    //function call
   }
   
   候选集合为{ f(char *), f(string) }
   
   using 和 using namespace可以使名字空间内的成员在某个scope内可见。
  d)函数的参数的类型为类T, 类T在名字空间NT中定义,则名字空间NT内可见(用using声明的也可以)的同名函数:
   namespace NT
   {
    void f(long);
    void f(float);
   }   
     
   namespace NT2
   {
    class T{};
    
    using namespace NT; //使namespace NT的成员在NT2中可见
    void f(float);  //这个与NT::f(float)造成“二义”
    
   }
   
   void caller()
   {
    NT2::T t;   
    f(t);    //参数t的类型T在名字空间NT内定义
   }
   
   候选集合为:{ NT::f(long), NT::f(float), NT2::f(float) }
   虽然f(float)在caller中不可见,但也会去名字空间NT中去找候选函数。
   
   注意:当T为typedef重定义的类型时,应把T还原后考虑,比如:
    namespace NT
    {
     class T{};
     
     void f(T);
    }
    
    namespace NT2
    {
     typedef NT::T t_type; //t_type实际上是NT::T
     
     void f(t_type);
    }
    
    void caller()
    {
     NT2::t_type t;   
     f(t);    //参数t的类型实际上为NT::T,所以在名字空间NT内定义
    }
    
    候选集合为:{ NT::T },没有NT2::T。
   只有当参数的类型为类class时才会执行这条,或者为指向类的指针、类的引用、指向类的成员的指针。
   当为int等内建类型时不会。
   
  d2)作为d)的延伸:
   会到类T的friend函数中找候选函数;
   会到类T的基类所在的名字空间中去找候选函数;
    namespace NT2
    {
     class Base{};
     
     void f(Base);
    }
    
    namespace NT
    {
     class T : public NT2::Base
     {
      friend void f(T);
     };
     
     void f(float);
    }
    
    void caller()
    {
     NT::T t;   
     f(t);    //参数t的类型T在名字空间NT内定义
    }
    
   候选集合为:{ f(Base), f(T), f(float) }
   
   实际上,类T的friend函数在类所在的namespace中是可见的,既使该friend函数在T中定义.
   比如: 
    namespace N
    {
     class T
     {
      friend void f(T) { cout << "friend of T" << endl; }
     };
     
     
     
     void g()
     {
      T t;
      f(t);  //ok. f在namespace N中可见
     }
     
     void f(T) {} //error。这个与T的friend函数f造成重复定义
    }
   
  d3) 如果函数有多个参数,则对每个参数都执行d),d2).
  注意:d),d2),d3)这几条叫:argument-dependent lookup
   这几条使得程序不是太容易读懂,应该不是好的写法。
 
  总结:a), b)步骤中找到了候选函数的话不会执行c), d),
     这是所谓的local hidden global.想想看,在C语言中没有class和namespace, 只有local和global之分,namespace只是将C语言的global细分,而class是将local细分。所以在C++中函数scope和class scope会hidden 全局和namespace
     c)这一步是最常见的,通常是调用全局函数,或者定义在namespace中的函数
     d)这步很让人迷惑,特别是d2)、d3)。但是也很常见。编译找不到函数调用时,会说:identifier not found, even with argument-dependent lookup
   
   找候选集合这一步最不直观,通常意想不到某个函数会在候选集合中而造成二义调用;或者想调用的函数不在候选集合中
   直观点的做法是用scope符"::"直接指定某一个函数,编译器会在指定的scope内找候选集合,而不去执行abcd这几步。
   
   如果候选集合为空,则编译器报错“identifier not found, even with argument-dependent lookup”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值