您的位置:首页 > 编程语言 > C语言/C++

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

2006-04-14 01:49 302 查看
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”
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: