作用域与实参相关的查找
首先对命名空间内部名字查找按一般由内向外依次每个作用域进行,有时候会发生名字内层对外层的隐藏,注意即可。
实参相关的查找与类类型参数:
例外:当我们给函数传递一个类类型的对象时,除了在常规的作用域查找外还会查找实参类所属的命名空间,对于传递类的引用或指针的调用同样有效
考虑以下:
std::string s;
std::cin>>s;//等价于operator>>(std::cin,s);
注意:
1.operator>>函数定义在标准库string中,string又定义在命名空间std中,
2.但是我们不用std:: 限定符和using 声明就可以调用operator>>。原因是,当我们给
函数传递一个类类型的对象时,
3.除了在常规的作用域查找外还会查找实参类所属的命名空间。这一例外对于传递类的
引用或指针的调用同样有效。
1.首先在operator>>的当前作用域查找,接着去外层作用域,再后因为>>表达式的形成是类类型,所以还会查找cin和s所属类的命名空间,
2.对于此调用,编译器会查找定义了istream和string的命名std,
3.当找到std时,string的 --非成员函数operator>>-- 正定义在std中,所以这个
例外不用std::operator>>就能正确调用
友元声明与实参相关的查找。当类声明了一个友元时,该友元声明并没有使得友元本身可见,要可见需在类外部声明。
注意:(成员函数作为友员,必须在当前该类之前声明,非成员函数和类的声明则不必在此之前)
一个另外的未声明的类或函数第一次出现在友元声明中,则认为它是最近的外层命名空间的成员,与上面的实参相关的查找规则结合会有想不到的效果
namespace A
{
class test
{
//在友元声明外没有其他声明,认为是命名空间A的成员,隐式的成为命名空间A的成员
//A::friend void func(); A::friend void func1(const test &t);
friend void func(); // 除非另有声明,否则不会被找到,
friend void func1(const test &t); // 根据实参相关的查找规则可以被找到
};
}
int main()
{
A::test t;
func(); // 错误,该友元func没有在类外部被声明,不可见
func1(t); // 正确,根据实参查找(会查找类所属的命名空间),通过在A::test中的友元声明找到A::func1
return 0;
}
两种using 对重载函数的作用域影响
重载与using声明:
uisng声明语句声明的是一个名字,而非一个特定的函数,当我们为函数书写using声明时,该函数的所有版本都被引入到当前作用域中。如果using声明出现在局部作用域中,则引入的名字将隐藏外层作用域的相关声明,如果using所在的作用域中已经有一个函数与新引入的函数同名且形参列表相同,将引发错误,产生冲突。
namespace A
{
void test(int);
}
using A::test(int); // 错误 uisng声明语句声明的是一个名字,而非一个特定的函数,不能指定形参列表
using A::test; // 正确
// 错误,使用using声明导致冲突
namespace A
{
void test(int) {}
}
using A::test;
void test(int) {} // 错误,使用using声明导致冲突
重载与using指示:
与using声明不同的是,对于using指示来说,引入一个与已有函数形参列表完全相同的函数并不会产生错误,可以存在冲突。此时只要我们指明调用的是命名空间中的函数版本还是当前作用域的版本即可。
namespace A
{
void test(int) {}
}
using namespace A;
void test(int) {} // 正确,using指示允许冲突,都是test候选函数集,只要指明调用
int main()
{
test(6);//错误,没有指明调用
::test(5);
A::test(5);
}
即使出现多个using指示,则来自每个命名空间的名字都会成为候选函数集的一部分
尽管可能声明位于不同作用域中,但他们都属于调用者的候选函数集