名字查找

  一个取T类型参数的函数常常与T类型本身定义在同一个名字空间里。因此,如果在使用一个函数的环境中无法找到它,我们就去查看它的参数所在的名字空间。例如,

    namespace Chrono {
        class Date { /* ... */ };

        bool operator==(const Date&, const std::string&);
        std::string format(const Date&);        // 做出字符串表示
        // ...
    }

    void f(Chrono::Date d, int i)
    {
        std::string s = format(d);              // Chrono::format()
        std::string t = format(i);              // 错误❌:在作用域里没有format(int)
    }

与显式地使用限定相比,这个查找规则能使程序员节省许多输入,而且又不会以使用指令(8.2.3节)那样的方式污染名字空间。这个规则对于运算符的运算对象(11.2.4节)和模板参数(C.13.8.4节)特别有用,因为在那里使用显式限定是非常麻烦的。

  请注意,名字空间本身必须在作用域里,而函数也必须在它被寻找和使用之前声明。

  当然,一个函数的参数可以来自多个名字空间。例如,

    void f(Chrono::Date d, std::string s)
    {
        if(d == s) {
            // ...
        }
        else if(d == "August 4, 1914") {
            // ...
        }
    }

对于这种情况,我们将在调用的作用域里(与往常一样),在每个参数(包括每个参数的类和基类)的名字空间里查找函数,而后对找到的所有函数执行普通的重载解析规则(7.4节)。特别地,对于上面的调用d==s,我们将在围绕f()的作用域里,在std名字空间里(这里有对于string的==定义),以及在Chrono名字空间里查找operator==。存在一个std::operator==,但它不以Date为参数;所以我们用Chrono::operator==(),它确实能用。另见11.2.4节。

  当一个类的成员调用一个命名函数时,函数查找时应当偏向于同一个类及其基类的其他成员,而不是基于其他参数的类型可能发现的函数。对运算符的情况则有些不同(11.2.1节、11.2.4节)。

🔚