Концепции великолепны, не поймите меня неправильно, но зачем нам для этого еще одно ключевое слово?
Рассмотрим следующий пример:
#include <type_traits>
template <typename T>
concept UnsignedConst = std::is_integral_v<T> && std::is_const_v<T>;
template <UnsignedConst T>
void foo(T bar);
С тем же успехом мы могли бы использовать следующий, на мой взгляд, более простой синтаксис:
/* ... */
template <typename T>
constexpr bool UnsignedConst = std::is_integral_v<T> && std::is_const_v<T>;
/* ... */
Под капотом концепции - это не что иное, как логические константы времени компиляции, зависящие от параметров шаблона (EDIT: НЕТ! См. принятый ответ). Даже requires-предложения можно использовать с шаблонами переменных, поскольку они представляют собой просто выражения, которые оцениваются как логические значения во время компиляции.
«концепции - это не что иное, как логические константы времени компиляции». Существуют правила включения, которые работают с понятиями (а не с базовыми условиями).
Еще не читал его, но я ожидаю, что здесь можно найти хорошую мотивацию: open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1886.pdf
Помимо правил включения, которые обеспечивают некоторую ценную ясность кода, ваш пример не демонстрирует, как UnsignedConst auto будет работать как тип. В основном его можно синтезировать на C++17, но он довольно уродлив и многословен, и людям трудно его читать.
Я знаю о шаблонах переменных, я просто думал, что концепции не нужны из-за их существования.
Тогда ваш вопрос не очень хорошо сформулирован, он говорит только о синтаксисе и о введении ключевого слова. С другой стороны, шаблоны переменных против концепций - это около 2 языковых функций.
Под капотом концепции — это не что иное, как логические константы времени компиляции, зависящие от параметров шаблона.
Это неправда.
Concept обладает особыми свойствами, которых нет у простых constexpr bool. В частности, concept не может быть специализированным. Эта неспособность быть специализированным делает возможными правила подчинения понятий. Это позволяет стандарту устанавливать правила для того, что считается «более специализированной» версией шаблона.
Учитывая следующее:
template<typename T>
concept A = atomic_constraint_a<T>;
template<typename T>
concept B = atomic_constraint_a<T> && atomic_constraint_b<T>;
В рамках правил концепций C++ ясно, что A включается в B. Это означает, что любой тип T, который удовлетворяет B, обязательно удовлетворяет A. Следовательно, если у вас есть два шаблона, один ограничен A, а другой — B, если вы передадите тип, удовлетворяющий B, будет выбран именно тот, который будет выбран, даже если есть версия для A.
Специализация разрушит это, потому что кто-то может прийти и специализировать B для типа U таким образом, что A<U> не включает B<U>.
Итак... что должно произойти тогда? Что происходит, когда кто-то пытается предоставить U вышеуказанному шаблону с конкурирующими ограничениями? Первичные ограничения говорят о наличии отношения подчинения, а специализированные - нет.
Что еще более важно, могу ли я даже написать две концепции с отношением подчинения и гарантировать, что это будет поддерживаться? То есть, в мире со специализацией, могу ли я полагаться на подчинение в своих интерфейсах шаблонов?
Нет, единственный способ сделать возможным подчинение — это запретить специализацию. Но шаблоны переменных могут быть специализированными. Итак, теперь вам нужна новая конструкция, которая говорит: «Я похож на эту штуку, но вы не можете специализировать меня». Мы могли бы заставить всех печатать template<typename T> [[nonspecialized]] inline constexpr bool concept_name = X, чтобы получить подчинение (и другие вещи, предусмотренные концепциями).
Или мы могли бы просто сказать template<typename T> concept concept_name = x и убрать кучу синтаксического шума.
Кроме того, concepts не могут быть членами классов. Это тоже важно, так как это означает, что вы не можете иметь де-факто специализацию концепции, специализируя класс вокруг концепции.
Кроме того, делая concept специальной синтаксической конструкцией, представленной собственным грамматическим токеном, это позволяет языку легче использовать concept в новых местах. Например, std::integral auto var_name = func();. Компилятор может видеть, что std::integral — это идентификатор, представляющий понятие. Если он увидит переменную constexpr bool, возможно, вы захотите использовать ее как концепцию. Или вы, возможно, намеревались использовать его как-то иначе. Делая концепции отдельными вещами, мы устраняем любую двусмысленность.
Это то, что позволяет нам по-разному использовать концепции типов в грамматике, что было бы... сложно без специализированного синтаксиса. Может быть, вы могли бы применить атрибут или что-то еще к constexpr inline bool, но опять же... зачем все ключевые слова, когда вы просто имеете в виду concept?
То, что вы предлагаете, уже существует. Это переменный шаблон, а не концепция