Clang (7, 8, trunk) rejects the following code
enum class E {};
inline static constexpr auto e = E{};
// inline static constexpr auto e = nullptr;
template<auto, int> class S;
template<int a, int b> class S<a, b> {};
template<int b> class S<e, b> {};
int main() {
S<0, 0> s;
}
with an error:
error: ambiguous partial specializations of 'S<0, 0>' note: partial specialization matches [with a = 0, b = 0] template<int a, int b> class S<a, b> {}; ^ note: partial specialization matches [with b = 0] template<int b> class S<e, b> {}; ^
Why is it ambiguous? How can e
match 0
? If I replace E{}
with nullptr
, Clang stops complaining. This looks like a Clang's bug. GCC compiles it just fine.
If it is a bug, what is a workaround? In my case, the auto
parameter can be either E
(and only one value E{}
) or int
. Then:
template<auto, int, typename> class S_impl;
template<int a, int b> class S_impl<a, b, int> {};
template<int b> class S_impl<e, b, E> {};
template<auto a, int b> using S = S_impl<a, b, decltype(a)>;
Is there a more succinct way?
Clang is doing the deduction wrong. It is similar to this bug, linked to this question (not exactly identical as you are using auto in template parameters which will prevent you to compile using stdc++14).
An interesting case is that it is not the case if it's a complete specialization; only on partial specialization :
#include <iostream>
enum class E {};
inline static constexpr auto e = E{};
template <auto a, int b>
class FOO;
template <int a, int b > class FOO<a, b> {};
template <int b> class FOO<e, b> {};
template <auto a, int b>
class BAR;
template <int a, int b > class BAR<a, b> {};
template <> class BAR<e, 0> {};
template <auto a>
class BAZ;
template <int a> class BAZ<a> {};
template <> class BAZ<e> {};
int main() {
// FOO <0, 0> foo; // <= Not Ok
BAR<0, 0> bar; // <= Ok
BAZ<0> baz; // <= Ok
}
Any solution that force the deduction of the type template parameter will work therefore your suggested solution is perfectly valid. IMHO, I would avoid using auto in template parameter when not necessary to improve readability :
template <typename T, T value, int> class S_impl; // <= this auto is not necessary
template <int a, int b> class S_impl<int, a, b> {};
template <int b> class S_impl<E, e, b> {};
// Either define S to use S<0,0> or directly use S_impl<int, 0, 0>
template <auto a, int b> using S = S_impl<decltype(a), a, b> // <= this auto is necessary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With