From clang 9.0.0 onwards, all versions of clang reject the following code when compiled with -std=c++17 (or -std=c++20):
#include <utility>
template<class L, std::size_t N>
struct A;
// #1
template
< template<class X, X...> class Holder
, class T, T... Cst
, std::size_t N
>
struct A<Holder<T, Cst...>, N>;
// #2
template
< template<class X, X...> class Holder
, class T, T... Cst
>
struct A<Holder<T, Cst...>, 0> {};
int main() {
A<std::index_sequence<>, 0> a{}; // supposed to select #2
}
Supposedly because instantiating A<std::index_sequence<>, 0>
is ambiguous:
<source>:20:33: error: ambiguous partial specializations of 'A<std::integer_sequence<unsigned long>, 0>'
A<std::index_sequence<>, 0> a{};
^
<source>:11:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>, N = 0]
struct A<Holder<T, Cst...>, N>;
^
<source>:17:8: note: partial specialization matches [with Holder = integer_sequence, T = unsigned long, Cst = <>]
struct A<Holder<T, Cst...>, 0> {};
Compiling with -std=c++14 doesn't exhibit the error. Compiling with gcc or clang 8.0.1 (or earlier) doesn't either. godbolt link
I don't understand why that would be ambiguous. I'd expect the following to correctly deduce Holder = Holder0, T=T0, Cst=Cst0, N=2
when calling f
:
template
< template<class X, X...> class Holder
, class T, T... Cst
, std::size_t N
>
void f(A<Holder<T, Cst...>, N>) {} // obtained from #1
template
< template<class X, X...> class Holder0
, class T0, T0... Cst0
>
struct Context {
static void g() {
f(A<Holder0<T0, Cst0...>, 0>{}); // obtained from #2
}
};
godbolt link
whereas the converse would usually fail for an arbitrary N:
template
< template<class X, X...> class Holder
, class T, T... Cst
>
void f(A<Holder<T, Cst...>, 0>) {} // obtained from #2
template
< template<class X, X...> class Holder0
, class T0
, std::size_t N
, T0... Cst0
>
struct Context {
static void g() {
f(A<Holder0<T0, Cst0...>, N>{}); // obtained from #1
}
};
godbolt link
From what I understand of N4659 (final working draft of c++17) and [temp.class.order]/1, that should imply that #2 is more specialized than #1.
Then again, I'm not good at reading the standard, and clang only giving me incorrect results with recent versions (both of the compiler and of the c++ standard) makes me second-guess myself.
So, is clang right or wrong here? If it's right, what am I missing?