【问题标题】:When does instantiation happens for explicit instantiation of a function template函数模板的显式实例化何时发生
【发布时间】:2021-04-30 07:04:54
【问题描述】:

您好,我想了解显式实例化。所以阅读不同的例子,但在一个例子中有一些疑问。下面给出了示例,我对这个特定示例有两个疑问。

文件 Application.cc 包含:

extern template int compare(const int&, const int&);
int i = compare(a1[0], a2[0]);// instantiation will appear elsewhere

文件 templateBuild.cc 包含:

template int compare(const int&, const int&);

还要注意函数模板比​​较是:

template<typename T, typename F = less<T>>
int compare(const T &v1, const T &v2, F f = F())
{
  if (f(v1,v2)) return -1;
  if (f(v2,v1)) return 1;
  return 0;
}

我的问题如下:

  1. 正如您在 Application.cc 文件的第 2 行中看到的那样(作为注释)实例化将出现在其他地方。但是这里我们使用模板函数int i = compare(a1[0], a2[0]);,我们知道每当我们使用模板函数时,编译器都会实例化它。那么为什么要写那条评论呢?同样在解释中写道

当编译器看到实例化定义(而不是声明)时,它 生成代码。因此,文件 templateBuild.o 将包含以下定义 将实例化与 int 进行比较。

所以我的问题是,如果编译器在看到实例化定义时生成代码,那么 templateBuild.o 将包含用 int 实例化的 compare 的定义,那么我们如何使用 compare(a1[0], a2[0]); 在 Application.cc 文件中使用 compare() ?我的意思是 compare() 模板还没有被实例化,那么在它被实例化之前我们如何使用它呢?

  1. 我的第二个问题是我应该在哪里写(放置) compare() 模板的内容。例如在头文件或 Application.cc 文件中? compare() 模板的内容是指我在示例中给出的第三个代码块。

【问题讨论】:

  • 要使用(调用)一个函数,只需要在相应的翻译单元中出现一个声明。这是(extern 的部分)。该声明指示编译器如何为函数调用生成机器代码,包括传递参数和可选地读取返回值。不需要函数定义来完成这个任务。
  • “我们知道,每当我们使用模板函数时,编译器都会实例化它”——我认为这不是真的。只有看到函数模板的定义,编译器才能实例化函数模板。
  • “代码仅在我们使用模板时生成(而不是在我们定义它时)”这是一本 C++ 11 书中关于函数模板的部分的准确引用。 @DanielLangr
  • 请注意来自en.cppreference.com/w/cpp/language/function_template 的以下引用:显式实例化声明(外部模板)防止隐式实例化:代码否则会导致隐式实例化必须使用程序中其他地方提供的显式实例化定义。 现场演示:godbolt.org/z/zxqozWMv7(未生成 f&lt;int&gt; 的机器代码)。你的书似乎有点不准确。我猜这是因为在实践中的绝大多数情况下,模板都没有显式地实例化。顺便说一句,那是哪本书?
  • 这本书是标准书(我假设)“C++ Primer Fifth eidtion”。还有另外两件事写在那里(我觉得矛盾),我在下面引用。引用1:“只要我们从不使用该函数,我们就可以声明一个未定义的函数”。引用 2:“当我们调用函数时,编译器只需要查看函数的声明”。现在 Quote1 和 Quote2 不是相互矛盾吗?不是使用它调用函数吗? @DanielLangr

标签: c++ c++11 templates instantiation explicit-instantiation


【解决方案1】:

我想您正在处理一个案例,其中函数模板在头文件中定义,然后包含在两个源文件中。例如(为简单起见,我删除了功能参数):

// compare.h

template<typename T>
int compare(const T &v1, const T &v2)
{
  if (v1 < v2) return -1;
  if (v2 < v1) return 1;
  return 0;
}
// Application.cc

#include <compare.h>

extern template int compare(const int&, const int&);
int i = compare(1, 2); 
// templateBuild.cc

#include <compare.h>

template int compare(const int&, const int&);

现在,可视化这两个源文件的翻译单元的样子很有用。

  1. Application.cc 的翻译单位:
template<typename T>
int compare(const T &v1, const T &v2)
{
  if (v1 < v2) return -1;
  if (v2 < v1) return 1;
  return 0;
}

extern template int compare(const int&, const int&);
int i = compare(1, 2);

当编译器翻译(编译)这个翻译单元时,它只看到显式实例化声明。如果它不存在,compare 函数调用将导致隐式定义实例化。但既然它在那里,就避免了这种隐式实例化,如here所写:

显式实例化声明(extern 模板)可防止隐式实例化:否则会导致隐式实例化的代码必须使用程序中其他地方提供的显式实例化定义。

因此,编译器仅生成用于调用 compare&lt;int&gt; 的机器代码,因为它是一个普通(非模板)函数,其声明但未在翻译单元中定义。

现场演示:https://godbolt.org/z/1c8jvvcv1

请注意,没有为compare&lt;int&gt; 生成机器代码。

  1. templateBuild.cc 的翻译单位:
template<typename T>
int compare(const T &v1, const T &v2)
{
  if (v1 < v2) return -1;
  if (v2 < v1) return 1;
  return 0;
}

template int compare(const int&, const int&);

这里,我们有一个显式的实例化定义,它导致函数模板被实例化为compare&lt;int&gt;

现场演示:https://godbolt.org/z/o9vxPvP75

现在,compare&lt;int&gt; 的机器码已经生成。

【讨论】:

  • cppinsights.io 可能有助于可视化:With extern, implicit instantiation without extern
  • 我想我对编译器和链接器的确切工作方式感到困惑。所以据我了解,这些是该程序(主要)如何工作的步骤。第 1 步:编译器看到语句extern template int compare(const int&amp;, const int&amp;);,它告诉它不需要隐式生成代码,除非它在程序中的某处遇到显式模板定义(可以是其他文件)。步骤2:它看到语句:int i = compare(a1[0], a2[0]); ,但由于步骤1中给出的原因没有实例化。 @DanielLangr
  • Step3:在templateBuild.cc中看到template int compare(const int&amp;, const int&amp;);并生成代码(或实例化compare)。现在我的问题是在语句 int i = compare(a1[0],a2[0]); 中初始化 i 我们需要从 compare 实例化返回的结果。那么这个结果是如何返回的呢?这将是链接器而不是编译器的工作吗?我的 3 步编译过程是否正确。你会在这个过程中添加什么? @DanielLangr
  • @JasonLiam 将参数传递给函数并从函数返回值的机制称为调用约定,由系统的 ABI 定义。例如,x86_64/Linux 上的int 返回值在eax 寄存器中传递。
【解决方案2】:

问题 1)

extern template int compare(const int&, const int&);

使用这一行,您可以抑制模板特化或其成员的隐式实例化。这就是第二行注释的意思。在当前示例中,显式实例化定义位于 templateBuild.cc 文件中。 你可以找到详细的解释here

问题 2)

您必须将模板类定义和声明放在同一个(头文件)文件中。 更多详情请查看this页面。

【讨论】:

  • 题目中没有涉及到类模板。
猜你喜欢
  • 2011-06-23
  • 2019-04-15
  • 2014-02-26
  • 2011-10-07
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多