【问题标题】:Using `extern template` with third-party header-only library将 `extern template` 与第三方仅标头库一起使用
【发布时间】:2020-08-12 02:33:06
【问题描述】:

我正在使用glm library,它是专为 3D 图形设计的数学实用程序的仅标题集合。通过在 Clang 和 ClangBuildAnalyzer 上使用 -ftime-trace,我注意到在实例化 glm 类型上花费了很多时间:

**** Templates that took longest to instantiate:
 16872 ms: glm::vec<4, signed char, glm::packed_highp> (78 times, avg 216 ms)
 15675 ms: glm::vec<4, unsigned char, glm::packed_highp> (78 times, avg 200 ms)
 15578 ms: glm::vec<4, float, glm::packed_highp> (78 times, avg 199 ms)

...

所以,我决定为glm 创建一个包装头/源代码对,并使用extern template 来避免不必要的实例化:

// glmwrapper.h

#pragma once

#include <glm.hpp>

extern template struct glm::vec<4, signed char, glm::packed_highp>;
extern template struct glm::vec<4, unsigned char, glm::packed_highp>;
extern template struct glm::vec<4, float, glm::packed_highp>;
// glmwrapper.cpp

template struct glm::vec<4, signed char, glm::packed_highp>;
template struct glm::vec<4, unsigned char, glm::packed_highp>;
template struct glm::vec<4, float, glm::packed_highp>;

现在,在我的项目中,我没有包含&lt;glm.hpp&gt;,而是包含"glmwrapper.h"。不幸的是,这并没有改变任何东西。使用-ftime-traceClangBuildAnalyzer 再次报告相同数量的实例化。也没有可测量的编译时间差异。

怀疑这是因为#include &lt;glm.hpp&gt; 实际上最终包含了模板定义,而在这一点上,随后的extern template 声明只是多余的。

有没有办法在不修改glm库的情况下实现我想要的?


在伪代码中,我有点想要这样的东西:

// glmwrapper.h (psuedocode)

#pragma once

#include <glm.hpp>

// Make definition of the templates unavailable:
undefine template struct glm::vec<4, signed char, glm::packed_highp>;
undefine template struct glm::vec<4, unsigned char, glm::packed_highp>;
undefine template struct glm::vec<4, float, glm::packed_highp>;

// Make declaration of the templates available:
extern template struct glm::vec<4, signed char, glm::packed_highp>;
extern template struct glm::vec<4, unsigned char, glm::packed_highp>;
extern template struct glm::vec<4, float, glm::packed_highp>;
// glmwrapper.cpp (psuedocode)

// Define templates only in the `.cpp`, not in the header:
template struct glm::vec<4, signed char, glm::packed_highp>;
template struct glm::vec<4, unsigned char, glm::packed_highp>;
template struct glm::vec<4, float, glm::packed_highp>;

【问题讨论】:

  • 这不是thisthis 的副本,因为它们都不能以适合我的方式解决我的问题。
  • 很好奇,我原以为您的初始方法会省略各种实例化,即使定义在显式实例化(重新)声明之前暴露,因为我假设(仅标题) lib 本身不应导致任何实例化。难道[dcl.spec.auto]/14 在这里适用,并且这些额外的实例化不是为了访问(重新实例化的)定义,而是为了auto 类型推导?在库本身(由于其他实例化)或您使用它的地方。
  • 我可能错了,但是删除glmwrapper.cpp 的显式实例化定义不是UB,不需要诊断吗?来自[temp.explicit]/11“作为显式实例化声明的主题并且也以其他方式在翻译单元中导致隐式实例化的方式使用的实体应成为显式实例化定义的主题程序;否则程序格式错误,不需要诊断。”.
  • @VittorioRomeo:是的,我的意思是随附的显式实例化声明,抱歉。您将无法在只能访问显式实例化声明(阻止实例化的if)的翻译单元中使用成员,因为该类将不完整。
  • @VittorioRomeo:你就是这么用的,但是效果你错了:需要Vector2D&lt;int&gt;才能完整的客户端必须实例化类模板尽管@ 987654349@ 因为他们不能使用.o 文件(在编译期间,而不是链接!)来了解成员和基础是什么。他们不必为类模板的非内联成员函数生成代码,但他们必须有自己的声明(以通常的按需方式)。

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


【解决方案1】:

不幸的是,没有办法避免这些实例化。 class 模板的显式实例化声明不会阻止(隐式)该模板的实例化;它只是阻止实例化它的非内联、非模板成员函数(通常都不是!),因为其他一些翻译单元将提供实际的函数符号和目标代码。

并不是看到模板定义会导致实例化(哪个特化会被实例化?)。原因是要求类完整的代码仍然需要知道它的 layout 和成员函数声明(用于重载决议),而且通常没有办法知道那些缺少实例化类的代码:

template<class T> struct A : T::B {
  typename std::conditional<sizeof(T)<8,long,short>::type first;
  typename T::X second;
  A() noexcept(T::y)=default;  // perhaps deleted
  using T::B::foo;
  void foo(T);
  // and so on…
};

void f() {A<C> a; a.foo(a.first);}  // …maybe?

这种“透明度”也延伸到模板化实体的several other kinds:如果编译 需要定义模板,则为链接器 生成的符号是无关紧要的。

好消息是 C++20 的 modules 应该可以帮助处理这样的情况:模块接口中的显式实例化definition 将导致典型实现缓存使用其余模块接口数据实例化类定义,避免在导入翻译单元时解析和实例化。模块还删除了类中定义的类成员和朋友的隐式inline(无论如何这在很长一段时间内都没有多大意义),增加了显式实例化声明的函数的数量(或者,换句话说,方便)防止隐式实例化。

【讨论】:

  • Is cppreference inaccurate then: "显式实例化声明(外部模板)跳过隐式实例化步骤:否则会导致隐式实例化的代码改为使用其他地方提供的显式实例化定义(如果不存在此类实例化,则会导致链接错误)。这可用于通过在所有使用它的源文件中显式声明模板实例化,并在其余文件中显式定义它来减少编译时间。 “?
  • 另外,这对代码膨胀意味着什么?如果extern template 所做的只是“仅仅阻止实例化其非内联、非模板成员函数”,那么我无法理解extern template 是如何有用的。
  • @VittorioRomeo:它对 function(和变量)模板很有用,无需实例化即可调用/使用。这扩展到非内联成员函数(其中在模块中有更多);成员函数模板当然也可以显式地实例化它们自己(当然还有额外的模板参数)。请记住,类模板成员函数只有在使用时才会单独实例化,这样可以减少臃肿。 Cppreference 没有错误,但它隐含了很多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-11
  • 1970-01-01
  • 1970-01-01
  • 2022-07-05
  • 2020-11-27
  • 1970-01-01
  • 2011-09-09
相关资源
最近更新 更多