面试题答案
一键面试1. 函数模板自动实例化时的符号解析
当函数模板发生自动实例化时,编译器按照以下步骤进行符号解析:
- 第一步:确定候选模板:编译器会查找所有与调用函数名匹配的函数模板声明。例如,假设有如下函数模板声明:
template <typename T>
void func(T param);
当调用func(10);
时,编译器会找到这个func
函数模板作为候选模板。
- 第二步:模板实参推导:编译器尝试从函数调用的实参推导出模板参数。对于上述
func(10);
的调用,编译器会推导出T
为int
。这个推导过程基于实参的类型和函数模板参数的声明方式。例如,如果函数模板参数是引用类型,推导规则会稍有不同。比如:
template <typename T>
void func(T& param);
调用int a = 10; func(a);
,这里T
会被推导为int
。如果是func(10);
这样的调用则会失败,因为不能将一个右值(临时值10
)绑定到一个非const
左值引用参数上。
- 第三步:实例化模板:一旦推导出模板参数,编译器就会生成一个具体的函数实例,即实例化模板。对于
func(10);
调用,编译器会生成一个void func(int param);
的函数实例。这个实例化过程涉及到将模板代码中的模板参数替换为实际推导出来的类型。
2. 多个同名不同模板参数函数模板的选择规则
当存在多个同名但不同模板参数的函数模板时,编译器依据以下规则选择合适的实例化版本:
- 最特例化优先:编译器优先选择最特例化的模板。例如:
// 通用模板
template <typename T>
void func(T param);
// 针对指针类型的特例化模板
template <typename T>
void func(T* param);
当调用int* ptr = nullptr; func(ptr);
时,编译器会选择第二个模板进行实例化,因为它对指针类型做了更特定的声明,比通用模板更特例化。
- 部分排序:如果没有最特例化的模板,编译器会进行部分排序。编译器会尝试从一个模板是否能比另一个模板接受更多的实参类型来判断。例如:
template <typename T1, typename T2>
void func(T1 param1, T2 param2);
template <typename T>
void func(T param1, T param2);
当调用func(10, 20);
时,第二个模板更特例化,因为它要求两个参数类型相同,所以编译器会选择第二个模板进行实例化。这是因为对于第二个模板,能够匹配它的实参组合比第一个模板要少,从而更特例化。
- 非模板函数优先:如果存在一个非模板函数与函数模板同名,且函数调用与非模板函数精确匹配,编译器会优先选择非模板函数。例如:
template <typename T>
void func(T param);
void func(int param);
当调用func(10);
时,编译器会选择非模板函数void func(int param);
,因为它是精确匹配,而不是实例化模板函数。