C++ 模版的名字查找问题 gaunthan Posted on Sep 25 2016 ? C++ ? > 当编译器碰到一个标识符时,它必须能够确定这个标识符所代表的实体的类型和作用域(如果它是一个变量,就是生存期)。模版的引入使得这个问题变得更加复杂。 <!--more--> ## 概述 当编译器首次看到一个模板定义时,它不知道这个模版的任何信息。只有当它看到模板的实例化时,它才能判断这个模版是否被正确使用了。这种状况导致了模板编译需要分两个阶段进行。 ## 模板中的名称 在第一阶段,编译器**解析模版定义**,寻找明显的语法错误,还要对它所能解析的所有名称进行解析。对于不依赖于模版参数的名称,编译器使用普通名称查找的方法解析它们,如果有必要,编译器也会依赖模板参数进行查找。它不能解析的名称就是所谓的**关联名**(dependent name),这些名称以某种方式依赖于模版参数。只有等到用实际参数来实例化模板时,这些名称才能被解析。因此,模板编译的第二个阶段是**模板实例化**。在这阶段,由编译器来决定是否使用模板的一个显式特化来取代基本的模板。 在用例子阐述这之间的细节之前,我们先了解几个概念。 ### 限定名 **限定名**(qualified name)指具有类名前缀,或者是被一个对象名加上点运算符修饰,或者是被一个指向某一对象的指针加一个箭头运算符所限定的名称修饰。如: ``` cpp MyClass::f(); // 具有类名前缀的名称f x.f(); // 对象名加点所修饰的名称f p->f(); // 对象指针加箭头所修饰的名称f ``` ### 关联参数查找(ADL) **关联参数查找**(argument-dependent lookup, ADL)机制起初是设计用来简化在名字空间中声明的非成员函数调用(包含运算符),如: ``` cpp #include <iostream> #include <string> // ... std::string s("hello"); std::cout << s << std::endl; // (1) // ... ``` 如果没有ADL机制的话,语句(1)是非法的,因为它没有指定要使用哪一个运算符函数,为此我们需要这样调用: std::operator<<(std::operator<<(std::cout, s), std::endl) 为了使最初的输出语句能够按照预期执行,ADL规定: > 当出现了某个非限定函数的调用,而该非限定函数却没有在一个(标准)作用域内进行声明时,编译器为了匹配这个函数声明,就会寻找它的每一个参数的名字空间来进行匹配。 没有ADL,名字空间的使用将会非常的不方便。如果想避开ADL,可以将函数名称置于一对圆括号中: (f)(x, y) // 避开ADL ### 小结 综上所述,若名称是关联的,则它的查找是在实例化时进行,非限定的关联名称除外:它是一个普通名称查找,在定义时进行。所有模板中的非关联名称被较早地查找,这种查找是在模板定义被解析时进行的。(若有必要,这种名称还有另一种实例化期间的查找,此时实际参数类型是已知的。) ## References - Meyers S. Effective C++[M]. 电子工业出版社, 2011. - StanleyB.Lippman, JoseeLajoie, BarbaraE.Moo, 等. C++ Primer 中文版 [J]. 2013. 赏 Wechat Pay Alipay C++ 模板的编译模型 C++ overloading 与 overriding