谁能提供模板实例化的比较或具体细节 在 GCC 和 MS 编译器的编译和/或链接时处理?这个过程有什么不同吗 在静态库、共享库和可执行文件的上下文中? 我找到了 this doc关于 GCC 如何处理它,但我不确定信息是否 仍然是指事物的当前状态。我应该使用标志吗 他们在编译我的库时建议在那里,例如-fno-implicit-templates?
我所知道的(可能不一定正确)是:
- 模板在实际使用时会被实例化
- 模板将作为显式实例化的结果进行实例化
- 重复实例化通常通过折叠重复实例化或将实例化延迟到链接时间来处理
最佳答案
实例化点
templates will be instantiated when actually used
不完全是,但大致上。实例化的精确点有点微妙,我将您委托(delegate)给名为 Point of instantiation 的部分。在 Vandevoorde/Josuttis 的精美书中。
但是,编译器不一定正确实现 POI:Bug c++/41995: Incorrect point of instantiation for function template
部分实例化
templates will be instantiated when actually used
这是部分正确的。对于函数模板是这样,但对于类模板,只有使用的成员函数被实例化。以下是格式良好的代码:
#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
指针,它是依赖的。除非你会使用
explicit instantiations
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 指令来执行。
关于c++ - GCC 和 MS 编译器的模板实例化细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7182359/