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

Strange template instantiation bug with non-type argument

以下 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 (trunk 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<> 的实例化时出错,但在 IntSpec<> 时不会出错。这是编译器错误、标准中的歧义还是我在编写代码时总是需要考虑的问题?如果可能,请提供参考。

编辑: 我的进一步分析发现以下内容适用于两个编译器:

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> >(); }

但是如果我将 &x 更改为 nullptr 我会得到 implicit instantiation of undefined template 'PtrSpec<MetaValue<int *, nullptr> >' with clang++.

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

§14.3.2/1:

A template-argument for a non-type, non-template template-parameter shall be one of:

[...]

  • a constant expression that evaluates to a null pointer value (4.10); or

[...]

§14.3.2/5:

The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.

[...]

  • for a non-type template-parameter of type pointer to object, qualification conversions (4.4) and the array-to-pointer conversion (4.2) are applied; if the template-argument is of type std::nullptr_t, the null pointer conversion (4.10) is applied. [Note: [...] However, both (int*)0 and nullptr are valid template-arguments for a non-type template-parameter of type “pointer to int.” — end note]

[...]