| I feel like one of the things a "Definitive Guide" to this feature needs to make clear, and maybe even emphasise, is a key way these are different than say Rust type Traits. Concepts, just like the SFINAE and constrexpr hacks you should discard in their favour, are about only what will compile and you, the C++ programmer, are always responsible for shouldering the burden of deciding whether that will do what you meant even if you have no insight into the types involved. Example: In C++ the floating point type "float" matches a concept std::totally_ordered. You can, in fact, compile code that treats any container of floats as totally ordered. But of course if some of them are NaN that won't work, because NaNs in fact don't even compare equal to themselves. You, the C++ programmer were responsible for knowing not to use this with NaNs. Whereas, Rust's floating point type f32 implements PartialOrd (saying you can try to compare these) but not Ord (a claim that all of them are totally ordered). If you know you won't use NaNs you can construct a wrapper type, and insist that is Ord and Rust will let you do that, because now you pointed the gun at your foot, and it's clearly your fault if you pull the trigger. This is a quite deliberate choice, it's not as though C++ could have just dropped in Rust-style traits, but I think a "Definitive Guide" ought to spell this out so that programmers understand that the burden the concept seems to be taking on is in fact still resting firmly on their shoulders in C++. The other side of this is, if you wrote a C++ type say Beachball that implements the necessary comparison operators the Beachball is std::totally_ordered in C++ 20 with no further work from you to clear up this fact. Your users might hope you'll document whether Beachballs are actually totally ordered or not though... I think this will likely prove to be a curse, obviously its proponents think it will work out OK or even a blessing. |
> Concepts, just like the SFINAE and constrexpr hacks you should discard in their favour, are about only what will compile and you, the C++ programmer, are always responsible for shouldering the burden of deciding whether that will do what you meant even if you have no insight into the types involved.
To be fair, that's not what makes them different from Rust. Unless there's something in Rust that I missed, it offers no guarantees that the implementation of the trait is consistent with its semantics.
Whether it's traits or concepts, it's still about compile-time type-checking, not actual contracts.