Search code examples
c++c++11g++clang++compiler-bug

Strange template instantiation bug with non-type argument


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++.


Solution

  • 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 and nullptr are valid template-arguments for a non-type template-parameter of type “pointer to int.” — end note]

    [...]