I am going through the cppreference guide to concepts and constraints, which seems to be the only one currently available. Under "atomic constraints", they give the following example about identical and non-identical constraints (with some editing to produce a MWE):
template<class T> concept bool Meowable = is_meowable<T>;
template<class T> concept bool BadMeowableCat = is_meowable<T> && is_cat<T>;
template<class T> concept bool GoodMeowableCat = Meowable<T> && is_cat<T>;
template<Meowable T>
void f1(T){ std::cout<<"Called #1\n";} // #1
template<BadMeowableCat T>
void f1(T){ std::cout<<"Called #2\n";} // #2
template<Meowable T>
void f2(T){ std::cout<<"Called #3\n";} // #3
template<GoodMeowableCat T>
void f2(T){ std::cout<<"Called #4\n";} // #4
void g(){
f1(0); // error, ambiguous: the is_meowable<T> in Meowable and BadMeowableCat forms distinct
// atomic constraints that are not identical (and so do not subsume each other)
f2(0); // OK, calls #4, more constrained than #3, GoodMeowableCat got its is_meowable<T> from Meowable
}
However, when I compile with g++ --std="c++1z" -fconcepts, the output is:
Called #2
Called #4
Is there somewhere in the standard (not in the Concepts TS but in the adopted version for C++20) where they specify this behaviour (or leave it specification defined), and if the guide is indeed right, what is the rationale behind such restrictive rules? I would assume concepts get normalized to the underlying constexpr bool.
Thanks in advance!
This question is IMHO different from this one, because the result from the execution contradicts the guide and apparently the standard, as the call to f1 should be ambiguous and therefore result in a compiler error, but it isn't, as apparently #2 subsumes #1. If this is not the case, I think it is worth an answer to clarify this divergence.