В некоторых случаях проверку индекса при доступе к элементу массива можно заменить проверкой при изменении индекса. Для этого в качестве типа индекса используется специальный класс, в котором определены необходимые операции, причем те из них, которые изменяют значение индекса, проверяются; при доступе же к элементу массива разрешено использовать только объекты этого индексного класса, а проверка не производится. Вот пример:
// safe_array.hpp
#ifndef safe_array_hpp
#define safe_array_hpp
#include <cstddef>
#include <stdexcept>
template <std::size_t N>
class safe_array_index {
std::size_t i_;
public:
explicit safe_array_index(std::size_t i)
{
if (i >= N)
throw std::out_of_range("initial value is out of range");
i_ = i;
}
safe_array_index& operator=(std::size_t i)
{
if (i >= N)
throw std::out_of_range("assigned value is out of range");
i_ = i;
return *this;
}
bool increment()
{
if (i_ == N - 1)
return false;
i_++;
return true;
}
operator std::size_t() { return i_; }
};
template <typename T, std::size_t N>
class safe_array {
T a_[N];
public:
T const& operator[](safe_array_index<N> i) const
{
return a_[i];
}
T& operator[](safe_array_index<N> i)
{
return a_[i];
}
};
#endif
// safe_array_test.cpp
#include "safe_array.hpp"
#include <iomanip>
#include <iostream>
int main()
{
safe_array<int, 10> a, b, c, d;
safe_array_index<10> i(0);
do {
a[i] = static_cast<int>(i);
b[i] = a[i] * a[i];
c[i] = b[i] * a[i];
d[i] = c[i] * a[i];
} while (i.increment());
i = 0;
do {
std::cout
<< std::setw(1) << a[i] << ' '
<< std::setw(2) << b[i] << ' '
<< std::setw(3) << c[i] << ' '
<< std::setw(4) << d[i] << '\n';
} while (i.increment());
}
Вместо 140 проверок при доступе к элементам массивов программа использует 22 проверки при изменении индекса, причем 20 из них практически не в счет, поскольку одновременно заменяют проверку условия окончания цикла. Оставшихся двух проверок в данном случае тоже можно избежать, добавив конструктор по умолчанию, инициализирующий индекс нулем, и функцию-член для замены присваивания нуля:
. . .
safe_array_index() : i_(0) {}
void reset() { i_ = 0; }
. . .
. . .
safe_array_index<10> i;
do {
a[i] = static_cast<int>(i);
b[i] = a[i] * a[i];
c[i] = b[i] * a[i];
d[i] = c[i] * a[i];
} while (i.increment());
i.reset();
. . .
(Инициализация нулем и присваивание нуля безопасны, поскольку C++ запрещает массивы нулевой длины.)
Хотя проверки во время исполнения программы не производятся, ошибки, связанные с обращением за пределы массива, исключены.