The declaration of SignedIntegral2
is ill-formed because of [temp.concept]/4:
A concept shall not have associated constraints.
And it's important to understand the reason for this. Concepts are basically predicates. Their job is to take a series of arguments (most commonly, a series of types) and say whether the concept is satisfied or not. But consider what answer these two different implementations would give:
SignedIntegral<int32_t>
is true
SignedIntegral<uint32_t>
is false
SignedIntegral<string>
is false
But:
SignedIntegral2<int32_t>
is true
SignedIntegral2<uint32_t>
is false
SignedIntegral2<string>
is... undefined
The whole point of concepts is to constrain. The proposed alternative, terse declaration in SignedIntegral2
constrains the type parameter T
to be Integral
. Since string
does not satisfy Integral
, we cannot even ask the question of if it's a SignedIntegral2
.
Put a different way, SignedIntegral
is a total function but SignedIntegral2
is a partial function that is only defined on Integral
types. This might be more clear if we write both to actually be functions:
template <typename T>
constexpr bool SignedIntegral() { return Integral<T> && is_signed_v<T>; }
template <Integral T>
constexpr bool SignedIntegral2() { return is_signed_v<T>; }
It is important that concepts always be total functions, which is why associated constraints are disallowed.
Note that it is surely possible as an extension to treat "undefined" as false
for the purposes of concepts satisfaction, but this would add extra wrinkles to the subsumption rules and it's surely non-trivial implementation complexity. It's certainly possible that some future standard might allow them. My crystal ball is currently at the shop, so I can't say for sure.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…