C++20stladvanced
std::regular (C++20 Addition)
C++20 · The Big Four · Compiler flag: -std=c++20
Explanation
Concept combining `semiregular` and `equality_comparable`. A regular type behaves like a built-in value type: it can be default-constructed, copied, moved, assigned, swapped, and compared for equality.
Real-World Scenario
Constrain elements of a generic value-semantic container to types that behave like proper values, enabling predictable copy and comparison semantics.
Quick Snippet
snippet.cpp
#include <concepts>
template<std::regular T>
class ValueStore {
std::vector<T> items;
public:
bool contains(const T& value) const {
return std::ranges::find(items, value) != items.end();
}
void add(T value) { items.push_back(std::move(value)); }
};Full Example
regular.cpp
// CodeNebula - C++20 Example: std::regular Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example regular.cpp
// std::regular<T> requires semiregular plus equality_comparable. A regular
// type behaves like a built-in type: default constructible, copyable,
// movable, and equality comparable. Think of int, string, vector.
#include <concepts>
#include <iostream>
#include <string>
#include <vector>
// A well-behaved regular type
struct Fraction {
int num, den;
Fraction() : num(0), den(1) {}
Fraction(int n, int d) : num(n), den(d) {}
bool operator==(const Fraction&) const = default;
};
std::ostream& operator<<(std::ostream& os, const Fraction& f) {
return os << f.num << "/" << f.den;
}
// A non-regular type (no operator==)
struct Opaque {
int data;
Opaque() : data(0) {}
};
// Container that requires regular element type
template <std::regular T>
class SimpleCache {
std::vector<T> items_;
public:
void store(const T& item) {
for (const auto& i : items_)
if (i == item) return; // needs equality_comparable
items_.push_back(item); // needs copy_constructible
}
std::size_t size() const { return items_.size(); }
void show() const {
for (const auto& i : items_) std::cout << " " << i << "\n";
}
};
template <typename T>
void check_regular(const char* name) {
std::cout << name << " - regular: " << std::regular<T>
<< ", semiregular: " << std::semiregular<T> << "\n";
}
int main() {
// Use SimpleCache with regular types
SimpleCache<int> int_cache;
int_cache.store(10);
int_cache.store(20);
int_cache.store(10); // duplicate, not added
std::cout << "Int cache (" << int_cache.size() << " items):\n";
int_cache.show();
SimpleCache<std::string> str_cache;
str_cache.store("hello");
str_cache.store("world");
str_cache.store("hello"); // duplicate
std::cout << "String cache (" << str_cache.size() << " items):\n";
str_cache.show();
SimpleCache<Fraction> frac_cache;
frac_cache.store({1, 2});
frac_cache.store({3, 4});
frac_cache.store({1, 2}); // duplicate
std::cout << "Fraction cache (" << frac_cache.size() << " items):\n";
frac_cache.show();
// Concept checks
std::cout << "Regularity checks:\n";
check_regular<int>("int");
check_regular<std::string>("string");
check_regular<Fraction>("Fraction");
check_regular<std::vector<int>>("vector<int>");
check_regular<Opaque>("Opaque");
return 0;
}
// Expected output:
// Int cache (2 items):
// 10
// 20
// String cache (2 items):
// hello
// world
// Fraction cache (2 items):
// 1/2
// 3/4
// Regularity checks:
// int - regular: 1, semiregular: 1
// string - regular: 1, semiregular: 1
// Fraction - regular: 1, semiregular: 1
// vector<int> - regular: 1, semiregular: 1
// Opaque - regular: 0, semiregular: 1
Run command
g++ -std=c++20 -Wall -Wextra -o example regular.cpp