Например, если у нас есть std::array
и мы создаем экземпляр элемента, который вышел за пределы, используя constexpr
компилятор не сообщит об ошибке:
constexpr int EvaluateSpecialArrayIndex(int a)
{ return a * sizeof(int); }
array<int, 5> arr;
cout << arr[98] << endl; //compiles fine
cout << arr[EvaluateSpecialArrayIndex(4)] << endl; //the same as above
Разве мы не можем как-то ограничить это?
Чтобы убедиться, что constexpr
функции оцениваются во время компиляции, вы должны заставить их быть, делая их результат constexpr
, Например:
#include <array>
int
main()
{
constexpr std::array<int, 5> arr{1, 2, 3, 4, 5};
int i = arr[6]; // run time error
}
Тем не мение:
#include <array>
int
main()
{
constexpr std::array<int, 5> arr{1, 2, 3, 4, 5};
constexpr int i = arr[6]; // compile time error
}
К сожалению, чтобы это действительно сработало, std::array
должен соответствовать спецификации C ++ 14, а не спецификации C ++ 11. Поскольку спецификация C ++ 11 не помечает const
перегрузка std::array::operator[]
с constexpr
,
Так что в C ++ 11 вам не повезло. В C ++ 14 вы можете заставить его работать, но только если оба array
и результат вызова оператора индекса объявляется constexpr
,
осветление
Спецификация C ++ 11 для индексации массива гласит:
reference operator[](size_type n);
const_reference operator[](size_type n) const;
И спецификация C ++ 14 для индексации массива гласит:
reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;
То есть constexpr
был добавлен в const
перегрузка для C ++ 14.
Обновить
И спецификация C ++ 17 для индексации массива гласит:
constexpr reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;
Цикл завершен. Вселенная может быть вычислена во время компиляции. 😉
Если вы знаете индекс массива во время компиляции, вы можете использовать std::get
с индексом, и это будут вызвать сбой компиляции, если вы вышли за пределы
std::array<int, 4> a{{1,2,3,4}};
std::get<4>(a); // out of bounds, fails to compile
Ошибка, которую я получаю от gcc-4.9, заканчивается:
error: static assertion failed: index is out of bounds
static_assert(_Int < _Nm, "index is out of bounds");
std::get
работает только с индексами константных выражений (индекс является аргументом шаблона), поэтому с std::array
он всегда может обнаружить за пределами во время компиляции.
Доступ к массиву на std::array
то же самое для обычного C-массива, он никогда не проверяет, является ли индекс действительным, он просто вызывает UB, если он находится вне диапазона. Если вы хотите ограничения, используйте std::array::at()
который бросает std::out_of_range()
исключение для значений, которые превышают границы массива.
arr.at(EvaluateSpecialArrayIndex(4)); // terminate called after throwing
// an instance of 'std::out_of_range'
Если вы хотите, чтобы ошибка во время компиляции использовалась std::get
:
std::get<EvaluateSpecialArrayIndex(4)>(arr); // error: static_assert failed
// "index is out of bounds"
Простой ответ, потому что это будет очень дорого для std::array
иметь отдельный constexpr
перегрузки для проверки такого рода вещей. Если вы хотите, вы можете написать свою собственную обертку вокруг std::array
который предлагает проверенный компиляцией доступ для констант времени компиляции. Что-то вроде:
template<typename T, size_t S>
class safeArray
{
std::array<T, S> m_arr;
public:
template<size_t A>
T& safeAt() {
static_assert(A < S, "Index out of bounds");
return m_arr[A];
}
// other funcs as needed
};
Затем вы можете сделать что-то вроде:
safeArray<int, 5> arr;
cout << arr.safeAt<98>() << endl; // compile error
Это может генерировать много функций, однако. В большинстве случаев, если ваш дизайн надежный, вам никогда не понадобится этот тип проверки.