Consider the following useless concept C
:
template<class T>
concept C = static_cast<T>(true);
If we pass an arbitrary type to C
in an unevaluated context, then all three compilers will compile successfully:
struct S {};
decltype(C<S>) x = 0;
But if we pass int
to C
in the unevaluated context:
decltype(C<int>) y = 0;
GCC still accepts it, while Clang and MSVC reject it with the same error message:
<source>:2:13: error: atomic constraint must be of type 'bool' (found 'int')
Is the above code still well-formed? Which compiler should I trust?
Concept names do not work on the basis of evaluating an expression as we would normally think of it. A concept name resolves to a boolean that tells if the constraint-expression is satisfied or not:
A concept-id is a prvalue of type bool, and does not name a template specialization. A concept-id evaluates to true if the concept's normalized constraint-expression is satisfied ([temp.constr.constr]) by the specified template arguments and false otherwise
Constraint expressions are broken down into atomic pieces. Fortunately, your constraint expression has only one atomic piece: static_cast<T>(true)
. The way we resolve whether an atomic constraint is satisfied is simple. There are several parts. Part one is:
To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied.
This is why the compilers allow the first one. static_cast<S>(true)
is not a valid expression, as there is no conversion from a bool
to an S
. Therefore, the atomic constraint is not satisfied, so C<S>
is false
.
However, static_cast<int>(true)
is a valid expression. So we move on to part 2:
Otherwise, the lvalue-to-rvalue conversion is performed if necessary, and E shall be a constant expression of type bool.
And that's where we run into the word "shall". In standard-ese, "shall" means "if the user provides code where this is not the case, there is a compile error". An int
is not a "constant expression of type bool
". Therefore, the code does not conform to this requirement. And a compile error results.
I imagine that GCC just treats the errors as substitution failures (that or it automatically coerces it into a bool
), but the standard requires MSVC/Clang's behavior of erroring out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With