实例化点
模板在实际使用时会被实例化
不完全是,但大致上。实例化的精确点有点微妙,我将您委托给 Vandevoorde/Josuttis 的精品书中名为 Point of instantiation 的部分。
但是,编译器不一定正确实现 POI:Bug c++/41995: Incorrect point of instantiation for function template
部分实例化
模板在实际使用时会被实例化
这是部分正确的。对于函数模板是这样,但对于类模板,只有使用的成员函数被实例化。以下是格式良好的代码:
#include <iostream>
template <typename> struct Foo {
void let_me_stay() {
this->is->valid->code. get->off->my->lawn;
}
void fun() { std::cout << "fun()" << std::endl; }
};
int main () {
Foo<void> foo;
foo.fun();
}
let_me_stay() 在语法上被检查(并且那里的语法是正确的),但不是在语义上(即它没有被解释)。
两阶段查找
不过,只有依赖的代码会在后面被解释;显然,在Foo<> 内,this 取决于实例化Foo<> 的确切模板ID,因此我们将Foo<>::let_me_alone() 的错误检查推迟到实例化时间。
但是如果我们不使用依赖于具体实例化的东西,代码一定是好的。因此,以下内容不是格式正确:
$ cat non-dependent.cc
template <typename> struct Foo {
void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; }
};
int main () {} // note: no single instantiation
Mine 是编译器完全未知的符号,不像this,编译器可以确定它的实例依赖关系。
这里的关键点是C++使用two-phase-lookup的模型,它在第一阶段检查非依赖代码,在第二阶段(和实例化时间)完成依赖代码的语义检查(这也是一个经常被误解或未知的概念,许多 C++ 程序员认为模板在实例化之前根本不会被解析,但这只是来自……Microsoft C++ 的神话)。
类模板的完整实例化
Foo<>::let_me_stay() 的定义起作用了,因为错误检查被推迟到以后,至于 this 指针,它是依赖的。除非你会使用
显式实例化
cat > foo.cc
#include <iostream>
template <typename> struct Foo {
void let_me_stay() { this->is->valid->code. get->off->my->lawn; }
void fun() { std::cout << "fun()" << std::endl; }
};
template struct Foo<void>;
int main () {
Foo<void> foo;
foo.fun();
}
g++ foo.cc
error: error: ‘struct Foo<void>’ has no member named ‘is’
不同翻译单位的模板定义
当你显式实例化时,你显式实例化。并使 all 符号对链接器可见,这也意味着模板定义可能驻留在不同的翻译单元中:
$ cat A.cc
template <typename> struct Foo {
void fun(); // Note: no definition
};
int main () {
Foo<void>().fun();
}
$ cat B.cc
#include <iostream>
template <typename> struct Foo {
void fun();
};
template <typename T>
void Foo<T>::fun() {
std::cout << "fun!" << std::endl;
} // Note: definition with extern linkage
template struct Foo<void>; // explicit instantiation upon void
$ g++ A.cc B.cc
$ ./a.out
fun!
但是,您必须显式实例化所有要使用的模板参数,否则
$ cat A.cc
template <typename> struct Foo {
void fun(); // Note: no definition
};
int main () {
Foo<float>().fun();
}
$ g++ A.cc B.cc
undefined reference to `Foo<float>::fun()'
关于两阶段查找的小提示:编译器是否真正实现了两阶段查找并不是由标准规定的。然而,为了保持一致,它应该像它一样工作(就像加法或乘法不一定必须使用加法或乘法 CPU 指令来执行。