Я пытаюсь разобраться concepts в C++20. Рассмотрим следующую (очень простую) концепцию:
template <typename T>
concept Float = requires
{
std::floating_point<T>;
};
Я могу написать шаблонный класс, используя эту концепцию двумя способами:
template <Float T>
class MyClass {
public:
MyClass(T val) : value{ val } {};
private:
T value;
};
Или я могу сделать
template <typename T> requires Float<T>
class MyClass {
public:
MyClass(T val) : value{ val } {};
private:
T value;
};
В этом конкретном примере они ведут себя точно так же. Хотя всегда ли это так? Существуют ли ситуации, в которых проще выразить какое-либо ограничение с помощью синтаксиса template <...> requires ...? Или это просто предпочтение/соглашение о стиле?
Единственное, о чем я могу сейчас подумать, это то, что использование синтаксиса template <...> requires ... может позволить вам представлять реляционные ограничения между несколькими аргументами шаблона, тогда как синтаксис template <CONCEPT T> позволяет вам представлять ограничение только для одного аргумента?
Любое руководство будет высоко оценено.





Использование имен концепций вместо имен типов всегда является сокращением того, что вы могли бы сделать в полной форме. Однако в некоторых случаях полный эквивалент оказывается более громоздким.
Например:
template <Concept auto value>
class foo {...};
//becomes
template <auto value> requires Concept<decltype(value)>
class foo {...};
Нет никакой разницы между:
template <Float T>
и
template <typename T> requires Float<T>
Они означают одно и то же. Если вы объявляете шаблон, а затем определяете его позже, вам придется использовать один и тот же синтаксис в обоих местах, но оба синтаксиса означают одно и то же.
Это чисто вопрос предпочтений. Синтаксис ограничения типа более ограничен (может использоваться только для ограничения типа), но может быть более читабельным.
Обратите внимание, что синтаксис ограничения типа может выражать ограничения нескольких типов. Например, типичный шаблон диапазонов:
template <typename I, typename S>
requires input_iterator<I>
and sentinel_for<S, I>
void algo(I, S);
можно записать сокращенно:
template <input_iterator I, sentinel_for<I> S>
void algo(I, S);
Интересно, что gcc позволяет использовать две формы ограничений взаимозаменяемо в объявлении и определении, требуя, чтобы они соответствовали семантически, а не лексически. Поскольку gcc — единственный из трех основных компиляторов, который допускает, что это, вероятно, расширение, а не стандартное требование: gcc.godbolt.org/z/zzvded9ro
Ааа, на самом деле меня это интересовало отдельно: как обрабатывать концепции/требования при объявлении и определении шаблонов в разных местах. Приятно знать, что синтаксис должен совпадать (хотя это несколько раздражает, что его нужно обрабатывать в обоих местах, особенно для большого класса, это делает очень громоздким объявление в
.hpp, а затем определение в.ipp