Скажем, я хотел написать общий класс, который поддерживает целое число, которое всегда остается между двумя значениями.Примерно так:
template<int Lower, int Upper>
class MyInt {
private:
int m_int;
public:
// Constructors, operators...
};
Инвариант класса - это Lower <= m_int <= Upper
.Конечно, в MyInt должны быть все обычные операции, которые обычно имеют целые числа, такие как операторы присваивания и арифметики.MyInt сгенерирует, если операция оставит его в состоянии, которое нарушает его инвариант.Однако во многих случаях это должно быть обнаружено во время компиляции.Рассмотрим пример этого кода:
int foo = 500;
constexpr int const bar = 500;
MyInt<0,100> a = 15; // OK
MyInt<0,100> b = foo; // Throws at runtime
MyInt<0,100> c = 500; // Compile error?
MyInt<0,100> d = bar; // Compile error?
MyInt<0,100> f = std::integral_constant<int, 500>; // Compile error
Для std::integral_constant
написать соответствующий конструктор просто.Но возможно ли во время компиляции обнаружить, что a
находится в пределах диапазона, а c
и d
- нет?Назначенные значения - это литералы, известные во время компиляции, или константы constexpr.
Я пробовал работать с SFINA и все такое, но я не смог найти способ перейти от семантики значений к аргументам шаблона, даже для этихслучаи, когда (я утверждаю) значения явно являются константами времени компиляции.
Верно ли для C ++ 17, что язык не предоставляет инструменты, необходимые для реализации этого?Если да, изменится ли это с наступающим C ++ 20?В случае, если то, что я ищу, невозможно, каковы причины этого?Мой интерес полностью образовательный, поэтому мне было бы интересно, если бы я что-то упустил (например, литералы, которые на самом деле не являются константами времени компиляции или что-то в этом роде).
Примечание: я знаю, что дело f
может быть сделаноменее уродливо, вводя пользовательский буквальный суффикс и требуя от пользователя напечатать что-то вроде этого:
MyInt<0,100> g = 15_constint; // OK
MyInt<0,100> h = 500_constint; // Compile error
Мне также известна возможность предоставить такой интерфейс:
MyInt<0,100> i;
i.assign<500>(); // Compile error
Однако оба эти обходные пути сопряжены с определенными накладными расходами на ввод текста и не обязательно являются идиоматическими C ++.