I'm learning C++ concepts, and trying to realize why the following does not compile (it's just a trivial and meaningless example demonstrating the point; tested with GCC-11.1):
#include <iostream>
struct non_negatable {
};
template <class T>
auto negate(T a) {
return -a;
}
template <class T>
concept is_negatable = requires(T t) {
//just `-a;` would do the job of course
negate(t);
};
template <class T>
auto do_negate(T) {
std::cout << "here we are\n";
}
template <is_negatable T>
auto do_negate(T t) {
return negate(t);
}
int main() {
non_negatable x;
do_negate(x);
}
The above concept attempts to call a function template, whose implementation would not compile. This fact causes a "hard error", rather than failing the concept.
I guess it works this way because the concept requirements may be failed only by the expressions' "immediate context" ill-formness (like what we could observe with SFINAE).
Is my understanding correct? Where does the Standard describe this point? Is there a way to "fail" a concept based on a function template implementation ill-formness?
Firstly, as you referred, negate<non_negatable>
is not a substitution failure since the error is not in the immediate context of the function, as such it's ill-formed. From 13.10.3.1/8 of C++20 standard:
If a substitution results in an invalid type or expression, type deduction fails.
An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.
[Note 4: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. — end note]
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
[Note 5: The substitution into types and expressions can result in effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such effects are not in the “immediate context” and can result in the program being ill-formed. — end note]
Secondly, 7.5.7.1/6 says:
[...]
[Note 1: If a requires-expression contains invalid types or expressions in its requirements, and it does not appear within the declaration of a templated entity, then the program is ill-formed. — end note]
[...]
So, your understanding seems correct to me. However, what you're asking in your last question boils down to whether there is a way to make an invalid expression in a template function body cause a substitution failure instead of an ill-formed program. I don't think it's possible.