C++成员函数模板的隐式实例化

标签 c++ templates

我想更好地了解编译器何时隐式实例化成员函数模板。

考虑以下示例:

// example.h

struct Parent {
  virtual void foo(double) {}
};

struct Child : Parent {
  void foo(double d) { bar(d); }

  template<typename T> void bar(T);

  virtual void baz();
}; 


// example.cpp
#include "example.h"
template <typename T> 
void Child::bar(T) {}

void Child::baz() {}

[g++|clang++] -c example.cpp 编译它, GCC 和 clang 都隐式实例化函数模板 Child::bar<double> .然而,以下看似微小的变化阻止了这一点:

  • 制作foo不是虚拟的
  • 制作Child不继承 Parent
  • 删除 baz
  • 制作baz不是虚拟的
  • 定义 baz在 header 中声明

对于隐式实例化何时发生,是否有任何合理简洁的解释,或者我是否需要费力地阅读标准的拷贝?我在 SO 中搜索了与隐式实例化相关的其他问题,但没有找到太多。我找到的最接近的是 this question ,但它是关于类模板(不是成员函数模板)中的虚函数。

明确一点:我知道可以通过在 header 中包含定义或显式实例化我需要的模板来避免这个问题。在深入研究为什么一个类(我无意中忽略了其显式实例化)仍然可以愉快地编译和链接时,这只是出于好奇。

最佳答案

我已将您的代码放入 Visual Studio 2013(child.h 和 child.cpp)并尝试了以下操作:

#include "child.h"
Child c1;
c1.bar(10.2);

这会产生一个 Unresolved external 错误,表明没有发生“隐式实例化”。表明在这种情况下 Visual Studio 和 G++ 之间存在明显的区别。 这是通过将 Child::foo 的代码移动到 child.cpp 文件中解决的。

因此简短的回答是: 您遇到的情况在很大程度上是特定于编译器的,为了可移植性,您不应该依赖这种行为。 根据 C++ 标准,最安全的方法是将模板定义放在 .h(或 .hpp)文件中,或者根据需要显式实例化模板。任何其他管理此问题的方法都可能会破坏某些编译器。

要进一步了解 Visual Studio 的行为,请查看以下内容:

也就是说,在类的定义中定义的任何函数都是隐式内联的。编译器可以选择延迟内联函数的实例化。这意味着在编译 child.obj 时 Child::foo(d) 永远不会被创建,这反过来意味着 bar 永远不会被实例化,所以这会导致链接阶段的编译问题。 考虑到 foo(double) 在技术上是一个虚函数,为什么 visual Studio 实际上可以做到这一点确实很奇怪,但似乎 Visual Studio 将 foo() 的实例化留给以后使用 Child 时使用。 例如:

Parent *p1 = new Child();

也会导致一个问题,因为此时编译器试图创建 Child::foo(double) 但由于缺少模板定义而无法创建。

根据您的结果,假设 GCC 将立即实例化内联函数(如果它们是虚拟的)。

您遇到的行为是多种因素的结合:

  1. 编译器如何处理隐式内联函数
  2. 如何为虚函数创建虚表
  3. 以及当编译器需要实例化虚成员函数时。

查看这些问题以获取更多信息:

所以:

  • 似乎允许编译器延迟内联函数的实例化直到使用,然后决定是否要“内联”它们或创建一个单独的函数。
  • 在使用类之前,编译器不需要完成对象所需的所有实例
  • 事实上,即使在创建对象时,编译器也可能没有为类准备好在代码中定义的所有内容。

我决定不详细回答你的具体问题,因为我的研究似乎表明你的代码工作是偶然的,这种行为不应该被依赖。

关于C++成员函数模板的隐式实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28114087/

相关文章:

c++ - BIT 移位运算符不工作

c++ - 阿姆斯特朗号码的代码不起作用

c++ - 详细类型说明符中的依赖于类型的嵌套名称说明符

c++ - Eigen 3 并根据模板参数重载新运算符以确保正确对齐

C++:将指向一个对象的成员函数的指针存储在另一个对象中

c++ - 将任何类型的表达式放在 C++ 的初始化列表中在语法上是否正确?

c++ - 为什么我的虚拟方法在 C++ 中被跳过?

C++异常处理错误输出

javascript - 在 ng-repeat 循环期间或循环后更改属性?

c++ - 嵌套类声明 : template vs non-template outer class