C++20: Define the Concepts Equal and Ordering
This is a cross-post from www.ModernesCpp.com.
In my last post, I defined the concept Equal. Now, I go one step further and use the concept Equal to define the concept Ordering.
Here is a short reminder of where I ended with my last post. I defined the concept of Equal and a function areEqual to use it.
template<typename T> concept Equal = requires(T a, T b) { { a == b } -> bool; { a != b } -> bool; }; bool areEqual(Equal auto fir, Equal auto sec) { return fir == sec; }
My Wrong Usage of the Concept Equal
I used the concept of Equal in my last post in the wrong way. The concept Equal requires that a and b have the same type but, the function areEqual allows that fir and sec could be different types that both support the concept Equal. Using a constrained template parameter instead of placeholder syntax solves the issue:
template <Equal T> bool fir, T sec) { fir == sec; }
Now, fir and sec must have the same type.
Thanks a lot to Corentin Jabot for pointing this inconsistency out.
Additionally, the concept Equal should not check if the equal and unequal operator returns a bool but something which is implicitly or explicitly convertible to a bool. Here we are.
template<typename T> concept Equal = requires(T a, T b) { { a == b } -> std::convertible_to<bool>; { a != b } -> std::convertible_to<bool>; };
I have to add. std::convertible_to is a concept and requires, therefore, the header <concepts>.
template <class From, class To> concept convertible_to = std::is_convertible_v<From, To> && requires(From (&f)()) { static_cast<To>(f()); };
The C++ 20 standard has already defined two concepts for equality comparing:
- std::equality_comparable: corresponds to my concept Equal
- std::equality_comparable_with: allows the comparison of values of different type; e.g.: 1.0 == 1.0f
The Challenge
I ended my last post by presenting a part of the type class hierarchy of Haskell.
The class hierarchy shows that the type class Ord is a refinement of the type class Eq. This can elegantly be expressed in Haskell.
class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool class Eq a => Ord a where compare :: a -> a -> Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool max :: a -> a -> a
Here is my challenge. Can I express such as relationship quite elegantly with concepts in C++20? For simplicity reasons, I ignore the functions compare and max of Haskell's type class. Of course, I can.
The Concept Ordering
Thanks to requires-expression, the definition of the concept Ordering looks quite similar to the definition of the type class Equal.
template <typename T> concept Ordering = Equal<T> && requires(T a, T b) { { a <= b } -> bool; { a < b } -> bool; { a > b } -> bool; { a >= b } -> bool; };
Okay, let me try it out.
// conceptsDefinitionOrdering.cpp #include <concepts> #include <iostream> #include <unordered_set> template<typename T> concept Equal = requires(T a, T b) { { a == b } -> std::convertible_to<bool>; { a != b } -> std::convertible_to<bool>; }; template <typename T> concept Ordering = Equal<T> && requires(T a, T b) { { a <= b } -> std::convertible_to<bool>; { a < b } -> std::convertible_to<bool>; { a > b } -> std::convertible_to<bool>; { a >= b } -> std::convertible_to<bool>; }; template <Equal T> bool areEqual(T a, T b) { return a == b; } template <Ordering T> T getSmaller(T a, T b) { return (a < b) ? a : b; } int main() { std::cout << std::boolalpha << std::endl; std::cout << "areEqual(1, 5): " << areEqual(1, 5) << std::endl; std::cout << "getSmaller(1, 5): " << getSmaller(1, 5) << std::endl; std::unordered_set<int> firSet{1, 2, 3, 4, 5}; std::unordered_set<int> secSet{5, 4, 3, 2, 1}; std::cout << "areEqual(firSet, secSet): " << areEqual(firSet, secSet) << std::endl; // auto smallerSet = getSmaller(firSet, secSet); std::cout << std::endl; }
The function getSmaller requires, that both arguments a and b support the concept Ordering, and both have the same type. This requirement holds for the numbers 1 and 5.
Of course, a std::unordered_set does not support ordering. The actual msvc compiler is very specific, when I try to compile the line auto smaller = getSmaller(firSet, secSet) with the flag /std:c++latest.
By the way. The error message is very clear: the associated constraints are not satisfied.
Of course, the concept Ordering is already part of the C++20 standard.
- std::three_way_comparable: corresponds to my concept Ordering
- std::three_way_comparable_with: allows the comparison of values of different type; e.g.: 1.0 < 1.0f
Maybe, you are irritated by the term three-way. With C++20, we get the three-way comparison operator, also known as the spaceship operator. <=>. Here is the first overview: C++20: The Core Language. I write about the three-way comparison operator in a future post.
Compiler Support
I learn new stuff by trying it out. Maybe, you don't have an actual msvc available. In this case, use the current GCC (trunk) on the Compiler Explorer. GCC support the C++20 syntax for concepts. Here is the conceptsDefinitionOrdering.cpp for further experiments: https://godbolt.org/z/uyVFX8.
What's next?
When you want to define a concrete type that works well in the C++ ecosystem, you should define a type that "behaves link an int". Such a concrete type could be copied and, the result of the copy operation is independent of the original one and has the same value. Formally, your concrete type should be a regular type. In the next post, I define the concepts Regular and SemiRegular.
Social Media Management für Unternehmen?? Optimierung Youtube, Instagram & Facebook Auftritte ?? Ich verwaltete Instagram- und FB-Seiten & baue Youtube Kan?le auf ...
5 年??