Reputation: 3744
For example if we have an std::array
and we instantiate an element that is out of bound using constexpr
the compiler wouldn't report error:
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
Can't we restrict this somehow?
Upvotes: 11
Views: 2787
Reputation: 218710
To ensure that constexpr
functions are evaluated at compile time, you must force them to be by making their result constexpr
. For example:
#include <array>
int
main()
{
constexpr std::array<int, 5> arr{1, 2, 3, 4, 5};
int i = arr[6]; // run time error
}
However:
#include <array>
int
main()
{
constexpr std::array<int, 5> arr{1, 2, 3, 4, 5};
constexpr int i = arr[6]; // compile time error
}
Unfortunately, for this to actually work, std::array
must conform to the C++14 specification, not the C++11 specification. As the C++11 specification does not mark the const
overload of std::array::operator[]
with constexpr
.
So in C++11 you're out of luck. In C++14, you can make it work, but only if both the array
and the result of calling the index operator are declared constexpr
.
Clarification
The C++11 specification for array indexing reads:
reference operator[](size_type n);
const_reference operator[](size_type n) const;
And the C++14 specification for array indexing reads:
reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;
I.e. constexpr
was added to the const
overload for C++14.
Update
And the C++17 specification for array indexing reads:
constexpr reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;
The cycle is now complete. The universe can be computed at compile-time. ;-)
Upvotes: 16
Reputation: 36792
If you know the array index at compile time, you could use std::get
with an index, and that will cause a compilation failure if you're out of bounds
std::array<int, 4> a{{1,2,3,4}};
std::get<4>(a); // out of bounds, fails to compile
The error I get from gcc-4.9 ends with:
error: static assertion failed: index is out of bounds
static_assert(_Int < _Nm, "index is out of bounds");
std::get
only works with constant expression indices (the index is a template argument), so with std::array
it can always detect out-of-bounds at compile time.
Upvotes: 8
Reputation: 96800
Array access on an std::array
is the same for a regular C-array, it never checks to see if the index is valid, it just invokes UB if it's out of range. If you want restrictions, use std::array::at()
which throws an std::out_of_range()
exception for values that exceed the array bounds.
arr.at(EvaluateSpecialArrayIndex(4)); // terminate called after throwing
// an instance of 'std::out_of_range'
If you want a compile-time error use std::get
:
std::get<EvaluateSpecialArrayIndex(4)>(arr); // error: static_assert failed
// "index is out of bounds"
Upvotes: 6
Reputation: 3816
Simple answer, because it would be very costly for std::array
to have separate constexpr
overloads to check for this sort of thing. If you want, you can write your own wrapper around std::array
that offers a compile-checked access for compile time constants. Something like:
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
};
Then, you can do something like:
safeArray<int, 5> arr;
cout << arr.safeAt<98>() << endl; // compile error
This can generate a lot of functions, however. Most of the time, if your design is sound, you won't ever need this type of check.
Upvotes: 2