The following C++11 code compiles with g++ 4.7.4, g++ 4.8.5, g++ 4.9.3 and g++ 5.3.0, but not with clang++ 3.7.1 or 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 only errors on instantiation of PtrSpec<>
but not on IntSpec<>
. Is this a compiler bug, an ambiguity in the standard or something I always need to consider when writing code? Please provide a reference, if possible.
EDIT: My further analysis found that the following works for both compilers:
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> >(); }
but if I change &x
to nullptr
I get implicit instantiation of undefined template 'PtrSpec<MetaValue<int *, nullptr> >'
with clang++.
This code should work fine, due standard. From 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][...]