我正在学习C++中的模板,因此尝试了不同的例子。下面给出了一个我无法理解其输出的例子:
template<typename T> void func(T p) {
g<T>(p); //ERROR
g(p); //NO ERROR?
}
int main()
{
}
当我试图编译上面的代码片段时,我得到了一个错误:
prog.cc: In function 'void func(T)':
prog.cc:2:1: error: 'g' was not declared in this scope
2 | g<T>(p); //ERROR
| ^
prog.cc:2:4: error: expected primary-expression before '>' token
2 | g<T>(p); //ERROR
| ^
我的问题是:
- 为什么会出现此错误
- 为什么我只得到语句
g<T>(p);
的错误,而没有得到g(p);
的错误?我认为写g(p);
相当于g<T>(p);
,因为由于模板参数推导,g
的模板参数将推导为T
模板使用两阶段名称查找。
首先,在模板定义时,检查模板的语法。
,因此g(p);
是正确的(从语法的角度来看((假设ADL找到的函数g
(。
对于g<T>(p);
,我们有(直到C++20(:
尽管函数调用可以通过ADL解析,即使普通查找一无所获,但对具有明确指定模板参数的函数模板的函数调用需要有通过普通查找找到的模板的声明(否则,遇到未知名称后面跟着小于字符是语法错误(
g<T>(p);
不正确,(仅解析为(g < T) > p;
(T
是一种类型,因此它是错误的((。
如果存在任何模板函数g
,那么g
可能被认为是模板,并且从语法角度来看代码是正确的(即使模板参数不匹配(。
案例1
在这里我们考虑声明:g<T>(p);
template<typename T> void func(T p) {
g<T>(p); //ERROR
}
int main()
{
}
在上面的代码片段中,名称g
是一个不合格的依赖名称。从来源:两相查找:
在第一阶段,当解析时,使用普通查找规则查找模板不合格的依赖名称。对于不合格的依赖名称,初始的普通查找(虽然不完整(用于决定该名称是否为模板。
现在让我们将其应用于语句g<T>(p);
。解析函数模板func
时,会遇到语句g<T>(p);
。由于g
是一个依赖限定名,根据上面引用的语句,名称g
是使用普通查找规则查找的。这使得编译器可以决定名称g
是否是模板。但是,由于在此之前没有名为g
的函数模板的声明,因此第一个尖括号<
被视为小于符号的,因此会产生您提到的错误。此外,请注意ADL是在第二阶段中完成的。但要实现这一点,我们必须首先成功解析函数func
的通用定义。[源:C++模板:完整指南:第250页最后一段]
请注意,编译器可以将错误延迟到实例化,这就是为什么有些编译器编译代码时不会出现任何错误的原因。演示。
情况2
在这里,我们考虑声明g(p);
。
template<typename T> void func(T p) {
g(p); // NO ERROR
}
int main()
{
}
在这种情况下,名称g
也是一个不合格的依赖名称。因此,根据我回答开头引用的语句,名称g
是使用普通查找规则查找的。但由于此时没有可见的g
,因此我们有一个错误。这里有两件事需要注意:
- 由于这一次您没有显式(通过使用尖括号(调用
g
,因此不存在语法错误 - 由于在这种情况下没有语法错误,并且程序中
func
也没有POI,因此这里没有延迟的错误,程序编译良好。然而,如果实例化func
,那么在实例化时会出现错误,称g
未声明