GCC 和 MS 编译器的模板实例化细节 [英] Template instantiation details of GCC and MS compilers

查看:22
本文介绍了GCC 和 MS 编译器的模板实例化细节的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

谁能提供模板实例化的比较或具体细节在 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 编译器的模板实例化细节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆