Clang编译报错调用未定义函数,GCC正常,是否属IFNDR范畴?
问题:GCC可正常编译的代码在Clang下链接报错
#include <cstdint> #include <array> #include <utility> std::size_t constexpr num = 5; using Doubles = std::array<double, num>; struct meow { template<typename V> static constexpr V value(V v, std::size_t) { return v; } template<typename V, std::size_t... Indices> static constexpr auto make(V v, std::index_sequence<Indices...>) -> std::array<V, sizeof...(Indices)> { return {{value(v, Indices)...}}; } Doubles doubles = make(3.1415926535897932384626433, std::make_index_sequence<num>()); }; int main() { meow m; return m.doubles.size(); }
这段代码在GCC中可以正常编译运行,但在Clang中会出现链接错误,提示找不到meow::make<double,0,1,2,3,4>的实现。请问这是什么原因?是否属于**未定义行为(IFNDR,即Ill-Formed, No Diagnostic Required)**的范畴?
原因分析
这是GCC和Clang对模板实例化时机的实现差异导致的,具体细节如下:
- 类成员模板
make是一个constexpr函数,它被用于初始化类的非静态成员doubles,而该初始化是在类定义内部完成的。 - C++标准中,当constexpr函数用于常量表达式上下文时需要隐式实例化,但这里的场景中,
doubles是非静态成员,其初始化发生在对象构造阶段,不属于严格的编译期常量初始化。 - GCC选择在编译阶段就实例化
make模板,而Clang则认为该实例化可以推迟到链接阶段,但由于模板定义在类内部且未被显式实例化,最终导致链接时无法找到对应的符号。
这种情况不属于IFNDR范畴,两种编译器的行为都符合标准,因为标准并未强制要求在此场景下必须在编译期完成模板实例化。
解决方法
可以通过以下几种方式修复Clang下的链接错误:
- 显式实例化模板:在类外部添加显式实例化声明,例如:
template std::array<double, 5> meow::make<double, 0,1,2,3,4>(double, std::index_sequence<0,1,2,3,4>); - 将模板定义移到类外部:把
make和value模板函数的定义放到类外部,让编译器更易处理实例化逻辑。 - 将成员声明为static constexpr:把
doubles改为static constexpr成员,这样初始化会进入常量表达式上下文,强制编译器在编译期完成模板实例化。
内容的提问来源于stack exchange,提问作者Bulletmagnet
相关产品推荐
相关产品推荐

