https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81043

--- Comment #3 from Mike Detwiler <shazamancan at gmail dot com> ---
I would like to share a temporary workaround that I have developed for this.
Consider the usual pattern for writing a partial specialization:

original decl:

template<ORIGINAL_PARAMS>
class my_class;

specialization:

template<SPECIAL_PARAMS>
class my_class<MODIFIED_PARAMS>;

If ORIGINAL_PARAMS, SPECIAL_PARAMS, and MODIFIED_PARAMS are all identical, then
it is an error, as it should be. Example of a true error:

template<typename T>
class my_class;

template<typename T> // <-- identical to ORIGINAL_PARAMS
class my_class<T>; // <-- T is unmodified, so MODIFIED_PARAMS is also identical

All three *PARAMS are identical, so it is an error. Example of a true
non-error:

template<typename T>
class my_class;

template<typename T> // <-- identical to ORIGINAL_PARAMS
class my_class<T*>; // <-- T is modified, so MODIFIED_PARAMS is NOT identical

Not all three *PARAMS are identical, so it is not an error. Another example of
a true non-error:

template<typename T> concept C = (sizeof(T) <= 4);

template<typename T>
class my_class;

template<C T> // <-- NOT identical to ORIGINAL_PARAMS (typename vs C)
class my_class<T>; // <-- T is unmodified, so MODIFIED_PARAMS is identical

Since not all three *PARAMS are identical it is not an error. Now for the tird
and final example of a true non-error (that GCC reports as a false error due to
this bug):

template<typename T> concept C0 = (sizeof(T) <= 4);
template<typename T> concept C1 = (sizeof(T) > 4);

template<typename T>
class my_class;

template<C0 T> // <-- NOT identical to ORIGINAL_PARAMS (typename vs C0)
class my_class<T>; // <-- T is unmodified, so MODIFIED_PARAMS is identical

template<C1 T> // <-- NOT identical to ORIGINAL_PARAMS (typename vs C1)
class my_class<T>; // <-- T is unmodified, so MODIFIED_PARAMS is identical

**BUT notice: MODIFIED_PARAMS for the first and second specialization are
identical

Neither partial specialization is an error because not all three *PARAMS are
identical in each specialization. GCC gets confused and reports this as an
error though. GCC only gets confused and falsely reports an error when two or
more specializations have identical MODIFIED_PARAMS like in the third example.

So my workaround is as follows:

#include <cstdint>

template<typename T> concept C0 = (sizeof(T) <= 4);
template<typename T> concept C1 = (sizeof(T) > 4);

template
<uint32_t, typename...>
using spec_id = void;

template<typename T, typename = void>
class my_class;

template<C0 T>
class my_class<T, spec_id<0, T>>;

template<C1 T>
class my_class<T, spec_id<1, T>>;

If you have two or more specializations where MODIFIED_PARAMS would be
unmodified like in the third example, you have to use spec_id<N,
SPECIAL_PARAMS> with unique N for each specialization. spec_id is NOT necessary
if for the following additional specializations (so just pass void directly):

template<C0 T>
class my_class<T*, void>;

template<C1 T, size_t N>
class my_class<T[N], void>;

This is because MODIFIED_PARAMS for these two is not identical to any other
MODIFIED_PARAMS in some other specialization.

Reply via email to