CodeNebula
C++20stladvanced

std::relation (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Concept that checks if a callable defines a binary relation over two types -- it must be a predicate that accepts two arguments (possibly of different types). Used to constrain comparators in algorithms.

Real-World Scenario

Constrain a generic merge function's comparator to be a valid relation over the element types.

Quick Snippet

snippet.cpp
#include <concepts>
#include <functional>

template<typename T, std::relation<T, T> Compare = std::less<T>>
T select_min(T a, T b, Compare comp = {}) {
    return comp(a, b) ? a : b;
}

auto m = select_min(5, 3);  // 3

Full Example

relation.cpp
// CodeNebula - C++20 Example: std::relation Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example relation.cpp

// std::relation<R, T, U> is satisfied when R is a binary predicate that
// can be called with all combinations of T and U arguments. Relations
// are used for comparisons, orderings, and equivalence checks.

#include <concepts>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

// Custom relation: checks if two strings share the same first character
struct SameInitial {
    bool operator()(const std::string& a, const std::string& b) const {
        return !a.empty() && !b.empty() && a[0] == b[0];
    }
};

// Sort using any relation as comparator
template <typename T, std::relation<T, T> Comp>
void bubble_sort(std::vector<T>& vec, Comp comp) {
    for (std::size_t i = 0; i < vec.size(); ++i) {
        for (std::size_t j = i + 1; j < vec.size(); ++j) {
            if (comp(vec[j], vec[i])) {
                std::swap(vec[i], vec[j]);
            }
        }
    }
}

// Group elements that are equivalent under a relation
template <typename T, std::relation<T, T> Rel>
std::vector<std::vector<T>> group_by(const std::vector<T>& items, Rel rel) {
    std::vector<std::vector<T>> groups;
    std::vector<bool> used(items.size(), false);
    for (std::size_t i = 0; i < items.size(); ++i) {
        if (used[i]) continue;
        groups.push_back({items[i]});
        for (std::size_t j = i + 1; j < items.size(); ++j) {
            if (!used[j] && rel(items[i], items[j])) {
                groups.back().push_back(items[j]);
                used[j] = true;
            }
        }
    }
    return groups;
}

int main() {
    // Sort with std::less (a standard relation)
    std::vector<int> nums = {5, 2, 8, 1, 9, 3};
    bubble_sort(nums, std::less<>{});
    std::cout << "Sorted ascending: ";
    for (int n : nums) std::cout << n << " ";
    std::cout << "\n";

    // Sort with std::greater
    bubble_sort(nums, std::greater<>{});
    std::cout << "Sorted descending: ";
    for (int n : nums) std::cout << n << " ";
    std::cout << "\n";

    // Sort strings by length
    std::vector<std::string> words = {"cherry", "fig", "apple", "date", "banana"};
    bubble_sort(words, [](const std::string& a, const std::string& b) {
        return a.size() < b.size();
    });
    std::cout << "\nBy length: ";
    for (const auto& w : words) std::cout << w << " ";
    std::cout << "\n";

    // Group by same initial letter
    std::vector<std::string> names = {"Alice", "Bob", "Anna", "Ben", "Carol", "Amy"};
    auto groups = group_by(names, SameInitial{});
    std::cout << "\nGrouped by initial:\n";
    for (const auto& g : groups) {
        std::cout << "  [";
        for (std::size_t i = 0; i < g.size(); ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << g[i];
        }
        std::cout << "]\n";
    }

    // Concept checks
    std::cout << "\nRelation checks:\n";
    std::cout << "less<> on int: " << std::relation<std::less<>, int, int> << "\n";
    std::cout << "SameInitial on string: "
              << std::relation<SameInitial, std::string, std::string> << "\n";

    return 0;
}

// Expected output:
// Sorted ascending: 1 2 3 5 8 9
// Sorted descending: 9 8 5 3 2 1
//
// By length: fig date apple cherry banana
//
// Grouped by initial:
//   [Alice, Anna, Amy]
//   [Bob, Ben]
//   [Carol]
//
// Relation checks:
// less<> on int: 1
// SameInitial on string: 1
Run command
g++ -std=c++20 -Wall -Wextra -o example relation.cpp