1. Strong structural equality should be static-assertable
The concept of "having strong structural equality" that was introduced by [P0732] will become important to programmers. Take this class type for example:
template < std :: size_t N > struct fixed_string { constexpr fixed_string ( const char ( & s )[ N + 1 ]) { std :: copy_n ( s , N + 1 , m_data ); } auto operator <=> ( const fixed_string & ) const = default ; char m_data [ N + 1 ]; };
This type’s
is a "structural comparison operator"
because it is defaulted, and invokes only other structural comparison operators. Since it yields
, it also has what we might call "strong structural ordering",
although this term is not introduced by P0732.
"Strong structural ordering" is a stricter requirement than P0732’s "strong structural equality,"
which is the prerequisite to use a user-defined type as a non-type template parameter.
C++ should permit the programmer to test the presence or absence of this property. Example:
static_assert ( std :: has_strong_structural_equality_v < fixed_string < 5 > > );
This permits maintainability-minded programmers to express their intention in code.
template < std :: size_t N > struct broken_fixed_string { constexpr broken_fixed_string ( const char ( & s )[ N + 1 ]) { std :: copy_n ( s , N + 1 , m_data ); } auto operator <=> ( const broken_fixed_string & rhs ) const { return std :: memcmp ( m_data , rhs . m_data , N + 1 ) <=> 0 ; } char m_data [ N + 1 ]; }; static_assert ( std :: has_strong_structural_equality_v < broken_fixed_string < 5 > > , "broken_fixed_string lacks the strong structural equality we expected" ); // ... possibly many lines of code here ... // ... possibly written by a different programmer ... template < auto V > struct A {}; A < broken_fixed_string ( "hello" ) > a ;
In the snippet above, we get a nice descriptive
failure, instead of an
unfriendly spew of diagnostics on the line that tries to instantiate
.
2. This feature requires support from the compiler
P1154R0 claimed that none of these type-traits could be implemented without a compiler builtin.
In fact,
can be implemented according to standard C++17:
template < auto > struct A {}; template < class T , template < T > class = A > using B = void ; template < class T , class = void > struct HasStrongStructuralEquality : std :: false_type {}; template < class T > struct HasStrongStructuralEquality < T , B < T >> : std :: true_type {}; static_assert ( HasStrongStructuralEquality < int >:: value ); static_assert ( ! HasStrongStructuralEquality < std :: string >:: value );
This code relies on subtle and maybe-uncertain rules governing when
is a valid argument for a parameter of type
.
-
GCC currently does not support this code — that is, they fail the second
.static_assert -
MSVC flatly rejects it because they don’t support
template parameters yet.auto -
Clang accepts this code. The worst that can be said of Clang is that they give the wrong answer for
, but that can easily be worked around on the library side.HasStrongStructuralEquality < int &&>
Right now the burden is on the application programmer to know this trivia and come up with workarounds for GCC and MSVC. We propose to simplify the programmer’s job by putting this trait into the standard library, where the burden will be on the library to get it right (probably by using a compiler builtin).
3. Provide a full complement of type traits
We propose these six type-traits, with their accompanying
versions.
For exposition purposes only, we provide sample implementations in terms of
a hypothetical compiler builtin
.
template < class T > struct has_structural_comparison : bool_constant < __has_structural_comparison ( T ) > {}; template < class T > struct has_strong_structural_ordering : bool_constant < __has_structural_comparison ( T ) && is_convertible_v < decltype ( declval < T > () <=> declval < T > ()), strong_ordering > > {}; template < class T > struct has_strong_structural_equality : bool_constant < __has_structural_comparison ( T ) && is_convertible_v < decltype ( declval < T > () <=> declval < T > ()), strong_equality > > {}; template < class T > struct has_weak_structural_ordering : bool_constant < __has_structural_comparison ( T ) && is_convertible_v < decltype ( declval < T > () <=> declval < T > ()), weak_ordering > > {}; template < class T > struct has_weak_structural_equality : bool_constant < __has_structural_comparison ( T ) && is_convertible_v < decltype ( declval < T > () <=> declval < T > ()), weak_equality > > {}; template < class T > struct has_partial_structural_ordering : bool_constant < __has_structural_comparison ( T ) && is_convertible_v < decltype ( declval < T > () <=> declval < T > ()), partial_ordering > > {};
4. Proposed wording
Add six new entries to Table 47 in [meta.unary.prop]:
Template Condition Preconditions
template < class T > struct has_structural_comparison ; For a glvalue of type
x , the expression
const T either does not invoke a three-way comparison operator or invokes a structural comparison operator (15.9.1).
x <=> x T shall be a complete type, cv , or an array of unknown bound.
void
template < class T > struct has_strong_structural_ordering ; is
has_structural_comparison_v < T > true
and the expressionis convertible to
x <=> x .
std :: strong_ordering T shall be a complete type, cv , or an array of unknown bound.
void
template < class T > struct has_strong_structural_equality ; is
has_structural_comparison_v < T > true
and the expressionis convertible to
x <=> x .
std :: strong_equality T shall be a complete type, cv , or an array of unknown bound.
void
template < class T > struct has_weak_structural_ordering ; is
has_structural_comparison_v < T > true
and the expressionis convertible to
x <=> x .
std :: weak_ordering T shall be a complete type, cv , or an array of unknown bound.
void
template < class T > struct has_weak_structural_equality ; is
has_structural_comparison_v < T > true
and the expressionis convertible to
x <=> x .
std :: weak_equality T shall be a complete type, cv , or an array of unknown bound.
void
template < class T > struct has_partial_structural_ordering ; is
has_structural_comparison_v < T > true
and the expressionis convertible to
x <=> x .
std :: partial_ordering T shall be a complete type, cv , or an array of unknown bound.
void
5. LEWG has jurisdiction
Before the San Diego 2018 meeting, the chair of LEWG questioned whether this paper should be handled by SG7 (Reflection and Compile-Time Programming). In response, the chair of SG7 has drafted D1354R0 "SG7 Guidelines for Review of Proposals," which lists this paper specifically in the "no review required" category. Quoting directly from D1354R0:
No Review Needed
A type trait that exposes properties of types that are already clearly observable in the behavior of the type within C++ code.
EXAMPLE 6 A trait that exposes strong structural equality.