Open t3nsor opened 1 year ago
Example of current implementation divergence:
struct S { void f() const noexcept; };
void g() noexcept;
template <auto> struct Mem;
template <void (S::*mem_fn)() const> struct Mem<mem_fn> {};
template <auto> struct NonMem;
template <void (*fn)()> struct NonMem<fn> {};
Mem<&S::f> m{};
NonMem<&g> nm{};
GCC rejects both cases, whereas Clang and MSVC both accepts them (applying [conv.fctptr]/1)).
May be a dupe of https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#697 .
Another example of implementation divergence:
template<typename T, int[T::size]>
struct A;
int x;
template<typename T>
struct A<T, &x> { };
A<int, &x> a;
Clang and EDG(ICC) reject, GCC accepts.
I don't know with absolute certainty what should happen. The explicit argument list is "<T, &x>", I think? And substituting gives "<T, int[T::size]>" which makes Clang complain, according to "The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the partial specialization." (according to [temp.spec.partial]p9.1. But that doesn't bother GCC.
Maybe related to https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1444 ? I have no idea.
Another case of implementation behavioral divergence removes the (supposed) violation of [temp.spec.partial]p9.1:
template<int N, int[N], typename T>
struct A;
int x;
template<typename T>
struct A<0, &x, T> { };
A<0, &x, bool> a;
Now GCC and EDG(ICC) accept, and Clang still rejects because of the zero-length array. I think it uses [temp.deduct]p3: "After this substitution is performed, the function parameter type adjustments described in [dcl.fct] are performed.". So the adjustments happen afterwards, not before.
May be a dupe of https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#697 .
Yeah, I think you're right. That issue doesn't have proposed wording, so maybe it's time to resurrect it—unless it's more complicated than I thought.
This underspecification is particularly problematic whenever decltype(auto)
template parameters are used. At the very least, we need to make it clear how to handle an example like this:
template <decltype(auto) R>
struct S; // undefined
template <int X>
struct S<X> { };
constexpr int x = 0;
constexpr const int& r = x;
S<r> s;
If the primary template were template <int X> struct S;
then S<r>
would just mean S<0>
. But in this case we have a partial specialization and it should be "subordinate" to the primary template and never allowed to have its arguments deduced directly from the template-id S<r>
. We need to first determine the identity of the specialization by looking at the primary template declaration (i.e. S<R>
where R
is of type const int&
and refers to x
) and then try to make S<X>
in the partial specialization identical to that S<R>
, which of course is not possible, so the partial specialization should not be used in this case. GCC wrongly accepts this snippet.
This underspecification is particularly problematic whenever
decltype(auto)
template parameters are used.
@t3nsor, https://github.com/cplusplus/CWG/issues/546 would clarify that the partial specialization is not a match.
Full name of submitter: Brian Bi
Reference (section label): [temp.spec.partial.match]
Issue description: [temp.spec.partial.match]/2 requires deduction to be done in order to determine whether a partial specialization declaration matches a template argument list, but doesn't explain which set of rules apply to such deduction.
Suggested resolution: Edit [temp.spec.partial.match]/2: