Представим, что есть класс, например, произвольного числа с плавающей запятой: с помощью шаблона пользователь может задать любые размеры экспоненты и мантиссы (size_t E, size_t M).
template <size_t E, size_t M>
class CustomFloat
{
// ...
};
Как ограничить пользователя и задать: \(E\geqslant 5\) и \(M\geqslant 10\)? То есть значения \(E=8,\) \(M=23\) подходят:
CustomFloat<8, 23> f{};
А, например, значения \(E=4,\) \(M=10\) должны приводить к ошибке компиляции.
Стандарт C++11 для таких целей ввёл шаблонную структуру std::enable_if.
template <size_t E, size_t M, std::enable_if<(E >= 5 && M >= 10), size_t>::type = 0>
class CustomFloat
{
// ...
};
Стандарт C++14 ввёл псевдоним std::enable_if_t, чтобы не приходилось писать ::type.
template <size_t E, size_t M, std::enable_if_t<(E >= 5 && M >= 10), size_t> = 0>
class CustomFloat
{
// ...
};
Пойдём немного вразрез с хронологией и посмотрим, как такое можно было проделать раньше; например, реализовать свой std::enable_if — my_enable_if и псевдоним члена type — my_enable_if_t.
template <bool B, typename T = void>
struct my_enable_if
{};
template <typename T>
struct my_enable_if<true, T>
{
typedef T type;
};
template <bool B, typename T = void>
using my_enable_if_t = typename my_enable_if<B, T>::type;
template <size_t E, size_t M, my_enable_if_t<(E >= 5 && M >= 10), size_t> = 0>
class CustomFloat
{
// ...
};
Концепты — возможность, которую специально ввели, чтобы решать эту проблему.
template <size_t E, size_t M>
requires (E >=5 && M >= 10)
class CustomFloat
{
// ...
};
Можно оформить концепт в виде самостоятельной сущности, чтобы использовать многократно.
template <size_t E, size_t M>
concept MinExponentAndMantissa = (E >= 5 && M >= 10);
template <size_t E, size_t M>
requires MinExponentAndMantissa<E, M>
class CustomFloat
{
// ...
};
Приложение: исходный код.