Параметризованные типы, такие как шаблоны C++, — это хорошо, но в большинстве случаев они могут быть параметризованы только другими типами.
Однако в C++ есть особый случай, когда шаблон можно параметризовать целым числом. Например, массивы фиксированной длины являются типичным вариантом использования:
template<typename T, int SIZE> class FixedArray
{
T m_values[SIZE];
public:
int getElementCount() const { return SIZE; }
T operator[] (int i) const {
if (i<0 || i>=SIZE)
throw;
else
return m_values[i];
}
};
void f()
{
FixedArray<float, 5> float_array; // Declares a fixed array of 5 floats.
//The size is known at compile time, and values are allocated on the stack.
}
В C++ разрешены только константные целые числа и указатели, но я думаю, что было бы интересно использовать любое значение для параметризации (поплавки, экземпляры классов и т. д.). Это может позволить выражать предварительные условия во время компиляции (обычно неформально указанные в документации) и автоматически проверять их во время выполнения. Например, вот шаблон «Интервал» на гипотетическом диалекте C++:
// Interval of type T between TMin and TMax.
template<typename T, T TMin, T TMax> class Interval
{
T m_value;
public:
Interval(int val) { *this = val; }
Interval& operator = (T val) {
//check that 'val is in [TMin, TMax] and set the value if so, throw error if not
if (val < TMin || val > TMax)
throw;
else
m_value = val;
return *this;
};
operator T() const { return m_value; }
}
// Now define a f function which is only allowing a float between O and 1
// Usually, that kind of function is taking a float parameter with the doc saying "the float is in 0-1 range". But with our Interval template we have
// that constraint expressed in the type directly.
float f(Interval<float, 0, 1> bounded_value)
{
// No need to check for boundaries here, since the template asserts at compile-time that the interval is valid
return ...;
}
// Example usage
void main();
{
float val = 0.5;
Interval<float, 0, 1> sample_interval = val; // OK. The boundary check is done once at runtime.
f(sample_interval); // OK. No check, because it is asserted by the type constraint at compile-time.
// This can prevent endless precondition testing within an algorithm always using the same interval
sample_interval = 3*val; // Exception at runtime because the constraint is no longer satisfied
f(sample_interval); // If I am here, I am sure that the interval is valid. But it is not the case in that example.
}
Тогда было бы интересно выразить отношения между этими типами. Например, выражение правила для присвоения интервала A другому интервалу B с другими границами или просто правило для присвоения значения интервалу с проверкой всего во время компиляции.
Есть ли язык с такой параметризацией (или похожим подходом), или его еще надо придумать? Какие-нибудь полезные исследовательские работы?