考虑以下简单模板功能:
template <typename T>
int compare(const T& lhs, const T& rhs) {
if (lhs<rhs) {
return -1;
}
else if (rhs<lhs) {
return 1;
}
else {
return 0;
}
}
我的课程讲师解释说,当我们使用模板函数时,我们应该隐式或显式地指定绑定到模板参数的模板参数:
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
int main() {
// implicitly specifying that T is int
cout<<compare(2,3)<<endl;
// explicitly specifying that T is string
cout<<compare<string>(string("something"),string("another"))<<endl;
}
推导出正确模板的过程称为“实例化”;实际模板参数用于生成稍后在运行时运行的相应版本的特定实例(在本例中为函数)。
他还提到实例化是“按需”发生的;例如,最后一个代码段将生成两个比较函数实例,一个用于int
,另一个用于string
。
这让我很奇怪,为什么编译器会抱怨这样的事情:
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
template <typename T>
int compare(const T& lhs, const T& rhs) {
if (lhs<rhs) {
return x; // deliberate compile-time error; x cannot be resolved
}
else if (rhs<lhs) {
return 1;
}
else {
return 0;
}
}
int main() {
// no calls for compare are made here
}
你认为编译器不应该对上面的编译时错误有任何问题,因为compare永远不会被实例化。但是,这不会编译...
由于没有提供模板参数,因此该模板函数对编译器没有任何意义(这是一个简单的比较函数,但考虑一个模板函数,它主要依赖于模板参数的类型......)
那么究竟什么是“实例化”呢?
答案 0 :(得分:5)
当编译器看到你的模板时,它已经在某种程度上解析并分析 。确切地说,已经完成的工作非常复杂,但除此之外,(模板参数无关)名称查找发生。这意味着编译器已经尝试找出x
是什么,并且他失败了。
编译器不试图解决的一个例子是T::x
究竟是什么,因为依赖 T
,你的模板参数。表达式x
不依赖于任何模板参数,这基本上是x
无法编译和T::x
will succeed的关键区别:
template <typename T>
int compare(const T& lhs, const T& rhs) {
if (lhs<rhs) {
return T::x;
}
else if (rhs<lhs) {
return 1;
}
else {
return 0;
}
}
当然,对于大多数类型T
,这将无法实例化,但这不是关键所在。你甚至可以为所有T
编写一个实例化失败的模板,但是这个问题的答案超出了范围。
答案 1 :(得分:1)
§14.6[temp.res] / p10,重点补充:
如果名称不依赖于模板参数(如 14.6.2),该名称的声明(或声明集)应在名称出现在模板中的范围内 定义强>;名称与声明(或声明)绑定 在那一点上找到并且此绑定不受声明的影响 在实例化时可见。
如果模板格式不正确,如果不能为其生成有效的专业化,则违反此规则需要诊断(因为它没有“无需诊断”)。
在您的代码中,x
是一个非依赖名称,因此compare
模板违反了此规则,并且需要符合要求的实现来生成诊断。