Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, netch80, Вы писали:
N>>На самом деле тут вообще ещё вопрос, при чём тут тьюринг-полнота языка на фазе трансляции, когда проверки всё равно надо впихивать в рантайм. Но если мы не сойдёмся по сказанному выше, то этот вопрос поднимать смысла нет.
S>Тут интереснее возможность не впихивать в рантайм избыточные проверки.
S>Например, при присванивании из RangeInteger<10, 20> в RangeInteger<5, 30> никакие проверки делать не надо.
S>В простых случаях вида RangeInteger<10, 50> a = 42; компилятору легко — после разворачивания шаблона сравнения if(42<10) будут выброшены.
S>А вот как отследить инварианты через границу двух разных типов?
Это как раз уже делается на существующих на данный момент средствах.
Clang имеет __builtin_assume(), который как раз говорит "считай данное условие гарантированным со стороны кодера". GCC (и Clang для совместимости) имеет __builtin_unreachable(), через который assume делается так:
#define assume(x) {if(!x) __builtin_unreachable(); }
И оптимизатор тоже умеет отбрасывать лишние проверки на таком основании.
Для такого RangeInteger мы можем написать
template <int Min, int Max> class RangeInteger {
...
operator underlying_int_t() const {
if (mValue < Min) {__builtin_unreachable();} // или __builtin_assume(mValue >= Min);
if (mValue > Max) {__builtin_unreachable();} // или __builtin_assume(mValue >= Max);
return mValue;
}
};
Главное не забыть делать реальные проверки при присвоении
(в конструкторе, operator=, где ещё надо — ты в курсе).
В твоём примере типа
RangeInteger<10, 20> i1;
...
RangeInteger<5, 30> i2 = i1;
компилятор получит гарантии 10 <= i1.mValue <= 20, и выкинет лишние проверки при присвоении i2.
Переносилось ли это в какие-то другие компиляторы (хоть в MSVC) — я не узнавал.