带有非类型参数的奇怪模板实例化错误
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]
[...]
以下 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
andnullptr
are valid template-arguments for a non-type template-parameter of type “pointer to int.” — end note][...]