实例化点
模板在实际使用时会被实例化
不完全是,但大概是这样。实例化的精确点有点微妙,我将您委托给名为的部分实例化点在 Vandevoorde/Josuttis 的好书中。
然而,编译器不一定正确实现 POI:Bug c++/41995:函数模板的实例化点不正确
部分实例化
模板在实际使用时会被实例化
这是部分正确的。对于函数模板来说确实如此,但是对于类模板来说,只有所使用的成员函数才会被实例化。以下是格式良好的代码:
#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
取决于具体的 template-idFoo<>
已实例化,因此我们推迟了错误检查Foo<>::let_me_alone()
直到实例化时间。
但如果我们不使用依赖于具体实例化的东西,代码一定是好的。因此,以下是not格式良好:
$ 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++ 使用的模型两阶段查找,它在第一阶段检查非依赖代码,而对依赖代码的语义检查在第二阶段(和实例化时间)完成(这也是一个经常被误解或未知的概念,许多 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 指令来执行一样。