GCC和MS编译器的模板实例化细节

任何人都可以提供GCC和MS编译器在编译和/或链接时如何处理模板实例的比较或具体细节? 这个过程在静态库,共享库和可执行文件中是不同的吗? 我找到了关于GCC如何处理它的这个文档,但我不确定这些信息是否仍然指的是事物的当前状态。 我应该在编译我的库时使用它们建议的标志,例如-fno-implicit-templates?

我所知道的(可能不一定是正确的)是:

  • 模板将在实际使用时被实例化
  • 模板将作为显式实例化的结果而被实例化
  • 通常通过折叠重复实例来处理重复实例化,或者通过推迟实例化直到链接时间来处理重复实例化


  • 实例化点

    模板将在实际使用时被实例化

    不完全,但粗略。 实例化的确切点有点微妙,我将它委托给Vandevoorde的/ Josuttis的好书中名为Point of instance的部分。

    但是,编译器不一定能正确实现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取决于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 ++使用两阶段查找模型,它在第一阶段检查非依赖代码,并且在第二阶段(和实例化时间)完成从属代码的语义检查(此实例化时间)也是一个经常被误解或未知的概念,许多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指令来执行一样。


    编辑 :事实证明,我下面写的是违反C ++标准。 这对于Visual C ++来说是正确的,但对于使用“两阶段名称查找”的编译器来说是错误的。

    据我所知,你说的是对的。 模板在实际使用时会被实例化(包括当被声明为另一个类型的成员时,但在函数声明中提到时(不包括正文))或者作为显式实例化的结果。

    模板的一个问题是,如果在几个不同的编译单元(.cpp文件)中使用相同的模板(例如矢量),编译器将重复在每个.cpp文件中实例化模板的工作,从而减慢编译速度。 IIRC,GCC有一些(非标准的?)机制可以用来避免这种情况(但我不使用GCC)。 但Visual C ++总是重复这项工作,除非您在预编译头文件中使用显式模板实例化(但即使这样也会减慢编译速度,因为较大的PCH文件需要较长的时间才能加载。)之后,链接程序将消除重复项。 注意 :下面的评论链接到一个页面,告诉我们并不是所有的编译器都以这种方式运行。 一些编译器推迟函数实例化,直到链接时间,这应该更有效率。

    第一次使用时,模板没有完全实例化。 特别是,模板中的函数在实际调用之前不会被实例化。 您可以通过向您正在使用的模板添加无意义的函数来轻松验证这一点:

    void Test() { fdsh "s.w" = 6; wtf? }
    

    除非明确地实例化模板或尝试调用该函数,否则不会出现错误。

    我期望静态库(和对象文件)将存储实例化的所有模板的目标代码。 但是,如果你的程序有一个特定的静态库作为依赖关系,那么你实际上不能调用其中已经实例化的模板函数,至少在VC ++中是这样,它总是需要模板类的源代码(带有函数体)为了调用它的功能。

    我不认为可以在共享库中调用模板函数(当您没有要调用的模板函数的源代码时)。

    链接地址: http://www.djcxy.com/p/15051.html

    上一篇: Template instantiation details of GCC and MS compilers

    下一篇: Float vs Decimal in ActiveRecord