带有非类型参数的奇怪模板实例化错误

Posted

技术标签:

【中文标题】带有非类型参数的奇怪模板实例化错误【英文标题】:Strange template instantiation bug with non-type argument 【发布时间】:2016-03-16 10:29:13 【问题描述】:

以下 C++11 代码可使用 g++ 4.7.4、g++ 4.8.5、g++ 4.9.3 和 g++ 5.3.0 编译,但不能使用 clang++ 3.7.1 或 clang++ 3.8.0(主干 254750):

template <typename F, F f> struct MetaValue ;

template <typename T> class IntSpec;
template <int V> class IntSpec<MetaValue<int, V> > ;

// note: template is declared here:
template <typename T> class PtrSpec;
template <void * V> class PtrSpec<MetaValue<void *, V> > ;

int main() 
  IntSpec<MetaValue<int, 0> >();

  // implicit instantiation of undefined template 'PtrSpec<MetaValue<void *, nullptr> >'
  PtrSpec<MetaValue<void *, nullptr> >();

Clang 仅在 PtrSpec&lt;&gt; 的实例化时出错,但在 IntSpec&lt;&gt; 上没有。这是编译器错误、标准中的歧义还是我在编写代码时总是需要考虑的问题?如果可能,请提供参考。

编辑:我的进一步分析发现以下两种编译器都适用:

template <typename F, F f> struct MetaValue ;

// note: template is declared here:
template<typename T> class PtrSpec;
template <int * V> class PtrSpec<MetaValue<int *, V> > ;

extern int x;

int main()  PtrSpec<MetaValue<int *, &x> >(); 

但如果我将&amp;x 更改为nullptr,我会使用clang++ 得到implicit instantiation of undefined template 'PtrSpec&lt;MetaValue&lt;int *, nullptr&gt; &gt;'

【问题讨论】:

使用 Clang 提交错误。他们会想要解决这个问题。 @Graznarak Nah,没有足够的动力。 【参考方案1】:

此代码应该可以正常工作,符合标准。来自N3242:

§14.3.2/1:

非类型、非模板的模板参数模板参数 应为以下之一:

[...]

计算结果为空指针值的常量表达式 (4.10);或

[...]

§14.3.2/5:

对每个用作 非类型模板参数。如果一个非类型 template-argument 不能 转换为相应 template-parameter 的类型,然后 程序格式不正确。

[...]

对于指向对象的类型指针的非类型 模板参数, 限定转换 (4.4) 和数组到指针的转换 (4.2) 适用;如果 模板参数std::nullptr_t 类型, 应用空指针转换(4.10)。 [注意: [...] (int*)0nullptr 对于非类型是有效的模板参数 template-parameter 类型为“指向 int 的指针”。 — 结束说明]

[...]

【讨论】:

知道为什么 Clang 会失败吗?

以上是关于带有非类型参数的奇怪模板实例化错误的主要内容,如果未能解决你的问题,请参考以下文章

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

可变参数模板错误:“在实例化中”(gcc 9.2)

Chapter16:模板

显式实例化模板类的显式实例化模板方法

带有 boost 的模板实例化:传递额外的参数