是否有人可以提供模板实例化的比较或具体细节 在GCC和MS编译器的编译和/或链接时处理?这个过程是不同的 在静态库,共享库和可执行文件的上下文中? 我发现this doc关于GCC如何处理它,但我不确定是否有这些信息 仍然指的是现状。我应该使用旗帜吗? 他们建议在编译我的库时,例如 -fno隐模板
我所知道的(可能不一定正确)是:
答案 0 :(得分:55)
模板将在实际使用时实例化
不完全,但粗略。实例化的确切点有点微妙,我将你委托给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’
当您显式实例化时,您将显式实例化。并使所有符号对链接器可见,这也意味着模板定义可能位于不同的翻译单元中:
$ 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指令来执行。
答案 1 :(得分:-2)
编辑:事实证明,我在下面写的内容与C ++标准相反。对于Visual C ++,情况确实如此,但对于使用“两阶段名称查找”的编译器则为false。
据我所知,你说的是对的。模板将在实际使用时进行实例化(包括在声明为另一种类型的成员时,但在函数声明中没有提到时(没有正文))或作为显式实例化的结果。
模板的一个问题是,如果在几个不同的编译单元(.cpp文件)中使用相同的模板(例如向量),编译器会重复在每个.cpp文件中实例化模板的工作,从而减慢编译速度。 IIRC,GCC有一些(非标准?)机制可用于避免这种情况(但我不使用GCC)。但Visual C ++总是重复这项工作,除非你在预编译的头文件中使用显式模板实例化(但即使这样也会减慢你的编译速度,因为更大的PCH文件需要更长的时间来加载。)然后,链接器会删除重复项。 注意:以下评论链接到page,告诉我们并非所有编译器都以这种方式运行。有些编译器将函数实例化推迟到链接时间,这应该更有效。
首次使用时,模板未完全实例化。特别是,模板中的函数在实际调用之前不会被实例化。您可以通过向正在使用的模板添加无意义函数来轻松验证这一点:
void Test() { fdsh "s.w" = 6; wtf? }
除非您明确地实例化模板,或尝试调用该函数,否则不会收到错误。
我希望静态库(和目标文件)将存储实例化的所有模板的目标代码。但是如果你的程序有一个静态库作为依赖项,你实际上不能调用已经在其中实例化的模板函数,至少在VC ++中是这样,VC ++总是需要模板类的源代码(带有函数体)。为了调用其中的函数。
我认为在共享库中调用模板函数是不可能的(当您没有要调用的模板函数的源代码时)。