This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115b. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-08-20
(From submission #482.)
Consider:
template<typename T>
concept C = sizeof(T) > sizeof(char);
template<typename T>
concept D = sizeof(T) > sizeof(int) ;
template<typename T>
struct A
{
template<typename U>
constexpr int f(U) requires C<U> { return 0; }
template<>
constexpr int f(int) requires D<T> { return 1; }
};
static_assert(A<int>().f(0) == 0); // #1
There is substantial implementation variance: GCC does not allow explicit specializations of function templates at class scope (contrary to the rule change introduced by issue 727), clang rejects them if a trailing-requires-clause is present, and EDG accepts, but ignores the constraint, causing #1 to fail.
Proposed resolution (reviewed by CWG 2024-03-01) [SUPERSEDED]:
Add a new paragraph before 13.9.4 [temp.expl.spec] paragraph 8 as follows:
An explicit specialization of a function shall not have a trailing requires-clause (9.3.1 [dcl.decl.general]). [ Example:
template<typename T> concept C = sizeof(T) <= sizeof(int); template<typename T> struct A { template<typename U> void f(U) requires C<U>; template<> void f(char); // OK template<> void f(short) requires (sizeof(T) >= 1); // error: trailing requires-clause not allowed }; template<> template<typename U> void A<int>::f(U) requires C<U> {} // OK, explicit specialization is a template-- end example ]
The placement of explicit specialization declarations for function templates ...
Add another example at the end of 13.9.4 [temp.expl.spec] paragraph 15 as follows:
[ Example:
template<typename T> struct D { template<typename U> static constexpr int f(U); // #1 template<typename U> static constexpr int f(U) requires (sizeof(T) == 1); // #2 template<> constexpr int f(int) // #3 { return 1; } }; template<> template<typename U> constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1) // #4 { return 0; } static_assert(D<char>::f(0) == 1); // overload resolution selects #2; #3 is a specialization for #2 static_assert(D<char[2]>::f(0) == 1); // overload resolution selects #1; #3 is a specialization for #1 static_assert(D<signed char>::f(0) == 1); // overload resolution selects #2; #3 is a specialization for #2 static_assert(D<signed char>::f(0.0) == 0); // overload resolution selects #2; #4 is a specialization for #2-- end example ]
Additional notes (April, 2024)
The phrasing "an explicit specialization of a function" does not make sense.
Possible resolution:
Add a new paragraph before 13.9.4 [temp.expl.spec] paragraph 8 as follows:
An explicit specialization that declares a function shall not have a trailing requires-clause (9.3.1 [dcl.decl.general]). [ Example:
template<typename T> concept C = sizeof(T) <= sizeof(int); template<typename T> struct A { template<typename U> void f(U) requires C<U>; template<> void f(char); // OK template<> void f(short) requires (sizeof(T) >= 1); // error: trailing requires-clause not allowed }; template<> template<typename U> void A<int>::f(U) requires C<U> {} // OK, explicit specialization is a template template<> template<> void A<short>::f(int) requires C<int> {} // error: trailing requires-clause for a declaration of a non-templated function-- end example ]
The placement of explicit specialization declarations for function templates ...
Add another example at the end of 13.9.4 [temp.expl.spec] paragraph 15 as follows:
[ Example:
template<typename T> struct D { template<typename U> static constexpr int f(U); // #1 template<typename U> static constexpr int f(U) requires (sizeof(T) == 1); // #2 template<> constexpr int f(int) // #3 { return 1; } }; template<> template<typename U> constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1) // #4 { return 0; } static_assert(D<char>::f(0) == 1); // overload resolution selects #2; #3 is a specialization for #2 static_assert(D<char[2]>::f(0) == 1); // overload resolution selects #1; #3 is a specialization for #1 static_assert(D<signed char>::f(0) == 1); // overload resolution selects #2; #3 is a specialization for #2 static_assert(D<signed char>::f(0.0) == 0); // overload resolution selects #2; #4 is a specialization for #2-- end example ]