問題描述
誰能提供模板實例化的比較或具體細節在 GCC 和 MS 編譯器的編譯和/或鏈接時處理?這個過程不一樣嗎在靜態庫、共享庫和可執行文件的上下文中?我找到了 this doc 關于 GCC 如何處理它,但我不確定如果信息仍然是指事物的現狀.我應該使用標志嗎他們在編譯我的庫時建議在那里,例如-fno-implicit-templates?
我所知道的(可能不一定正確)是:
- 模板會在實際使用時實例化
- 模板將作為顯式實例化的結果進行實例化
- 重復實例化通常通過折疊重復實例化或將實例化推遲到鏈接時間來處理
實例化點
<塊引用>模板會在實際使用時實例化
不完全是,但大致上.實例化的精確點有點微妙,我將您委托給名為 實例化點,在 Vandevoorde/Josuttis 的好書中.
然而,編譯器不一定正確實現 POI:Bug c++/41995:函數模板的實例化點不正確
<小時>部分實例化
<塊引用>模板會在實際使用時實例化
那是部分正確的.對于函數模板來說是這樣,但是對于類模板,只有使用到的成員函數才會被實例化.以下是格式良好的代碼:
#include 模板結構 Foo {void let_me_stay() {這個->是->有效->代碼.下車->下車->我的->草坪;}void fun() { std::cout <<有趣()"<<std::endl;}};int主(){Foo富;foo.fun();}
let_me_stay()
在語法上被檢查(并且那里的語法是正確的),但在語義上沒有被檢查(即它不被解釋).
兩階段查找
然而,只有依賴代碼被稍后解釋;顯然,在 Foo<>
中,this
依賴于實例化 Foo<>
所使用的確切模板 ID,因此我們推遲了Foo<>::let_me_alone()
的錯誤檢查,直到實例化時間.
但是如果我們不使用依賴于具體實例化的東西,代碼一定是好的.因此,以下內容不格式良好:
$ cat non-dependent.cc模板結構 Foo {void I_wont_compile() { 我的-> 是-> 有效-> 代碼.下車->下車->我的->草坪;}};int main () {}//注意:沒有單個實例化
Mine
對編譯器來說是一個完全未知的符號,與 this
不同,編譯器可以確定它的實例依賴關系.
這里的關鍵點是 C++ 使用了 兩階段查找,它在第一階段檢查非依賴代碼,依賴代碼的語義檢查是在第二階段(和實例化時間)完成的(這也是一個經常被誤解或未知的概念,許多 C++ 程序員認為模板在實例化之前根本不會被解析,但這只是神話來自......., 微軟 C++).
類模板的完整實例化
Foo<>::let_me_stay()
的定義有效,因為錯誤檢查被推遲到以后,至于 this
指針,它是依賴的.除非你會使用
顯式實例化
cat >foo.cc#include 模板結構 Foo {void let_me_stay() { this->is->valid->code.下車->下車->我的->草坪;}void fun() { std::cout <<有趣()"<<std::endl;}};模板結構 Foo;int主(){Foo富;foo.fun();}g++ foo.cc錯誤:錯誤:struct Foo"沒有名為is"的成員
不同翻譯單位的模板定義
當您顯式實例化時,就是顯式實例化.并使所有符號對鏈接器可見,這也意味著模板定義可能駐留在不同的翻譯單元中:
$ cat A.cc模板結構 Foo {無效的樂趣();//注意:沒有定義};int主(){Foo().fun();}$貓 B.cc#include 模板結構 Foo {無效的樂趣();};模板 void Foo::fun() {std::cout <<樂趣!"<<std::endl;}//注意:帶有外部鏈接的定義模板結構 Foo;//在 void 上顯式實例化$ g++ A.cc B.cc$ ./a.out樂趣!
但是,您必須顯式實例化所有要使用的模板參數,否則
$ cat A.cc模板結構 Foo {無效的樂趣();//注意:沒有定義};int主(){Foo<float>().fun();}$ g++ A.cc B.cc對 `Foo::fun()' 的未定義引用
<小時>
關于兩階段查找的小說明:編譯器是否真正實現了兩階段查找并不是由標準規定的.然而,為了符合要求,它應該像它一樣工作(就像加法或乘法不一定必須使用加法或乘法 CPU 指令來執行一樣.
Could anyone provide a comparison or specific details of how is template instantiation handled at compile and/or link time in GCC and MS compilers? Is this process different in the context of static libraries, shared libraries and executables? I found this doc about how GCC handles it but I'm not sure if the information is still referring to the current state of things. Should I use the flags they suggest there when compiling my libraries e.g. -fno-implicit-templates?
What I know (might not necessarily be correct) is that:
- templates will be instantiated when actually used
- templates will be instantiated as a result of explicit instantiations
- duplicate instantiation is usually handled by folding duplicate instantiations, or by deferring instantiation until link time
Point of instantiation
templates will be instantiated when actually used
Not exactly, but roughly. The precise point of instantiation is a bit subtle, and I delegate you over to the section named Point of instantiation in Vandevoorde's/Josuttis' fine book.
However, compilers do not necessarily implement the POIs correctly: Bug c++/41995: Incorrect point of instantiation for function template
Partial instantiation
templates will be instantiated when actually used
That is partially correct. It is true for function templates, but for class templates, only the member functions that are used are instantiated. The following is well-formed code:
#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()
is checked syntactically (and the syntax there is correct), but not semantically (i.e. it is not interpreted).
Two phase lookup
However, only dependent code is interpreted later; clearly, within Foo<>
, this
is dependent upon the exact template-id with which Foo<>
is instantiated, so we postponed error-checking of Foo<>::let_me_alone()
until instantiation time.
But if we do not use something that depends on the specific instantiation, the code must be good. Therefore, the following is not well-formed:
$ 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
is a completely unknown symbol to the compiler, unlike this
, for which the compiler could determine it's instance dependency.
The key-point here is that C++ uses a model of two-phase-lookup, where it does checking for non-dependent code in the first phase, and semantic checking for dependent code is done in phase two (and instantiation time) (this is also an often misunderstood or unknown concept, many C++ programmers assume that templates are not parsed at all until instantiation, but that's only myth coming from, ..., Microsoft C++).
Full instantiation of class templates
The definition of Foo<>::let_me_stay()
worked because error checking was postponed to later, as for the this
pointer, which is dependent. Except when you would have made use of
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’
Template definitions in different units of translation
When you explicitly instantiate, you instantiate explicitly. And make all symbols visible to the linker, which also means that the template definition may reside in different units of translation:
$ 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!
However, you must explicitly instantiate for all template arguments to be used, otherwise
$ 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()'
Small note about two-phase lookup: Whether a compiler actually implements two-phase lookup is not dictated by the standard. To be conformant, however, it should work as if it did (just like addition or multiplication do not necessarily have to be performed using addition or multiplication CPU instructions.
這篇關于GCC 和 MS 編譯器的模板實例化細節的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!