【发布时间】: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>;
现在,在我的项目中,我没有包含<glm.hpp>,而是包含"glmwrapper.h"。不幸的是,这并没有改变任何东西。使用-ftime-trace 和ClangBuildAnalyzer 再次报告相同数量的实例化。也没有可测量的编译时间差异。
我怀疑这是因为#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>;
【问题讨论】:
-
很好奇,我原以为您的初始方法会省略各种实例化,即使定义在显式实例化(重新)声明之前暴露,因为我假设(仅标题) lib 本身不应导致任何实例化。难道[dcl.spec.auto]/14 在这里适用,并且这些额外的实例化不是为了访问(重新实例化的)定义,而是为了
auto类型推导?在库本身(由于其他实例化)或您使用它的地方。 -
我可能错了,但是删除
glmwrapper.cpp的显式实例化定义不是UB,不需要诊断吗?来自[temp.explicit]/11:“作为显式实例化声明的主题并且也以其他方式在翻译单元中导致隐式实例化的方式使用的实体应成为显式实例化定义的主题程序;否则程序格式错误,不需要诊断。”. -
@VittorioRomeo:是的,我的意思是随附的显式实例化声明,抱歉。您将无法在只能访问显式实例化声明(阻止实例化的if)的翻译单元中使用成员,因为该类将不完整。
-
@VittorioRomeo:你就是这么用的,但是效果你错了:需要
Vector2D<int>才能完整的客户端必须实例化类模板尽管@ 987654349@ 因为他们不能使用.o文件(在编译期间,而不是链接!)来了解成员和基础是什么。他们不必为类模板的非内联成员函数生成代码,但他们必须有自己的声明(以通常的按需方式)。
标签: c++ c++11 templates extern explicit-instantiation