C++类模板作为函数参数或成员未使用其成员是否会被实例化
核心前提规则
C++ 类模板的实例化遵循按需分层、最小实例化原则,显式实例化声明(extern template)会阻止当前翻译单元生成对应特化的任何隐式实例化代码,相关定义将交由其他翻译单元提供,链接阶段统一寻址。
其中类模板的实例化分为两个层级:
- 类布局实例化:仅需要确认类的大小、内存布局,不需要用到任何成员函数时触发,不会实例化任何类成员函数
- 成员函数实例化:只有当成员函数被实际调用、或需要其定义存在时才会单独触发
疑问逐一解答
为什么只有显式实例化声明没有定义,程序仍然编译通过?
你添加的extern template struct Pow<int>;已经告知编译器:Pow<int>的实例化定义不在当前翻译单元,无需在此处生成。且你的代码全程没有需要用到Pow<int>成员函数定义的场景,编译器仅需要从前面的模板声明中计算出Pow<int>的大小即可满足当前编译需求,自然不会报缺失定义错误。为什么函数定义的参数用了
Pow<int>,也没有要求Pow<int>的定义?
你此前的认知有误:函数定义的参数为模板类型时,仅需要该模板类型是完整类型(即可以确认大小、布局),不需要用到该类型的成员函数,仅会触发类布局层级的实例化,不会要求生成完整的模板实例化定义。加了extern template后,连当前翻译单元的类布局实例化都会被阻止,编译器只要能从模板定义推导大小即可,不会要求你提供Pow<int>的实例化定义。取消注释后编译失败,是否说明仅作为参数类型、类成员类型时不会触发实例化?
是的。
- 作为函数参数类型时,仅需要确认
Pow<int>的大小,不会用到其成员函数,不会触发成员函数实例化 - 作为类成员类型时,
Pow<int> pi{}是聚合初始化,Pow没有自定义构造函数,只需要分配对应大小的内存即可,也不会触发任何成员函数实例化
取消注释后调用了operator(),此时需要该成员函数的定义存在,而extern template阻止了当前翻译单元生成该函数的实例化代码,你又没有在其他翻译单元提供显式实例化定义,链接时就会找不到符号导致失败。
- 三个场景的实例化规则:
void f(Pow<int>);:仅为函数声明,只需要Pow<int>是已声明的类型即可,不需要其为完整类型,不会触发任何实例化void f2(Pow<int>){}:函数定义,值参数需要确认Pow<int>的大小,仅会触发Pow<int>的类布局层级隐式实例化,不会实例化任何成员函数;如果加了extern template,该实例化也会被阻止,不会要求提供实例化定义void f3(Pow<int> Pwi){ std::cout << Pwi(10);}:调用了operator()成员函数,除了类布局实例化外,会触发operator()成员函数的实例化;如果加了extern template,当前翻译单元不会生成该函数的定义,链接时需要其他翻译单元提供Pow<int>的显式实例化定义,否则会报错。
- 定义
Foo f;时会不会触发Pow<int>的实例化?
定义Foo对象时需要确认Foo的整体大小,所以需要确认成员Pow<int> pi的大小,会触发Pow<int>的类布局层级隐式实例化,但因为没有用到Pow<int>的任何成员函数,所以不会实例化任何成员函数。如果加了extern template,该类布局实例化也会被阻止,编译器仅需要从模板定义推导Pow<int>的大小即可。
内容的提问来源于stack exchange,提问作者Itachi Uchiwa
相关产品推荐
相关产品推荐

