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

cel*_*vek 44 c++ compiler-construction gcc templates visual-studio

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

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

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

Seb*_*ach 56


实例化点

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

不完全,但粗略.实例化的确切点有点微妙,我将你委托给Vandevoorde/Josuttis的精美书中的实例化点.

但是,编译器不一定正确实现POI:错误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();
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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’
Run Code Online (Sandbox Code Playgroud)


不同翻译单位的模板定义

显式实例化时,您将显式实例化.并使链接器可以看到所有符号,这也意味着模板定义可能位于不同的转换单元中:

$ 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!
Run Code Online (Sandbox Code Playgroud)

但是,您必须显式实例化所有要使用的模板参数,否则

$ 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()'
Run Code Online (Sandbox Code Playgroud)

关于两阶段查找的小注释:编译器是否实际实现两阶段查找不受标准规定.要符合标准,但是,它应该工作,如果它没有(就像加法或乘法不一定要使用加法或乘法CPU指令来执行.