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 areEqual(T 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 requiresexpression, 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 } > std::convertible_to<bool>;
{ a < b } > std::convertible_to<bool>;
{ a > b } > std::convertible_to<bool>;
{ a >= b } > std::convertible_to<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 threeway. With C++20, we get the threeway comparison operator, also known as the spaceship operator. <=>. Here is the first overview: C++20: The Core Language. I write about the threeway 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.
Thanks a lot to my Patreon Supporters: Meeting C++, Matt Braun, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Richard Ohnemus, Frank Grimm, Sakib, Broeserl, António Pina, Markus Falkner, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, and Lawton Shoemake.
Thanks in particular to: 


Get your ebook at Leanpub:
The C++ Standard Library


Concurrency With Modern C++


Get Both as one Bundle






With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages. I also included more than 120 source files. 

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.
I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more than 140 source files.


Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.
In sum, you get more than 700 pages full of modern C++ and more than 260 source files presenting concurrency in practice.

Get your interactive course

Modern C++ Concurrency in Practice

C++ Standard Library including C++14 & C++17



Based on my book "Concurrency with Modern C++" educative.io created an interactive course.
What's Inside?
 140 lessons
 110 code playgrounds => Runs in the browser
 78 code snippets
 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.
What's Inside?
 149 lessons
 111 code playgrounds => Runs in the browser
 164 code snippets
 25 illustrations

Read more...