【问题标题】:C++ Implicit instantiation of member function templates成员函数模板的 C++ 隐式实例化
【发布时间】:2015-01-23 16:14:42
【问题描述】:

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

考虑以下示例:

// 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&lt;double&gt;。但是,以下看似微小的更改阻止了这一点:

  • 使foo 不是虚拟的
  • 使Child 不继承自Parent
  • 删除baz
  • 使baz 不是虚拟的
  • 用标题中的声明定义baz

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

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

【问题讨论】:

  • 它很简单——它们仅在被调用时被实例化。您拥有的不同案例集仅决定何时实际需要该参考。当你有一个虚函数时,它需要在虚表中创建一个条目。在 foo 中调用 bar 会强制它实例化它。如果没有 baz,它就不需要 Child 的 vtable。使 baz 内联不应该影响它 - 这可能是优化的结果?如果你从 Child 继承了一个孙子并给它一个虚拟函数 - 现在内联 baz 不应该阻止实例化

标签: c++ templates


【解决方案1】:

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

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

这会产生一个未解决的外部错误,表明没有发生“隐式实例化”。表明在这种情况下 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. 而当编译器需要实例化虚成员函数时。

有关更多信息,请参阅以下问题:

所以:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    相关资源
    最近更新 更多