Предпочитайте определять концепты через паттерны использования, а не через простой синтаксис
Причина
Определение более читаемо и непосредственно соответствует тому, что пишет пользователь. Преобразования учитываются. Не нужно помнить имена всех трейтов типов.
Пример
Можно соблазниться определить концепт Equality вот так:
template<typename T> concept Equality = has_equal<T> && has_not_equal<T>;
Очевидно, что лучше и проще использовать стандартный equality_comparable, но — для наглядности примера — если нужно определить такой концепт, предпочтите:
template<typename T> concept Equality = requires(T a, T b) {
{ a == b } -> std::convertible_to<bool>;
{ a != b } -> std::convertible_to<bool>;
// axiom { !(a == b) == (a != b) }
// axiom { a = b; => a == b } // => означает "влечёт"
};
вместо определения двух бессмысленных концептов has_equal и has_not_equal просто в качестве вспомогательных при определении Equality. «Бессмысленных» в том смысле, что мы не можем задать семантику has_equal в изоляции.
Контроль
???
Интерфейсы шаблонов
На протяжении многих лет программирование с шаблонами страдало от нечёткого разграничения между интерфейсом шаблона и его реализацией. До появления концептов это разграничение не имело прямой языковой поддержки. Тем не менее интерфейс шаблона — это критически важный концепт, контракт между пользователем и реализатором, и его следует тщательно проектировать.