10

我正在使用glmlibrary,它是用于 3D 图形的仅限标题的数学实用程序集合。通过-ftime-trace在 Clang 和 上使用ClangBuildAnalyzer,我注意到很多时间都花在实例化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>;

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

怀疑这是因为#include <glm.hpp>实际上最终包含了模板定义,而在这一点上,后续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>;
4

1 回答 1

3

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

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

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?

这种“透明性”也扩展到了其他几种模板化实体:如果编译需要定义模板,则为链接器生成的符号是无关紧要的。

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

于 2020-05-13T03:41:51.577 回答