AnArrayOfFunctions
AnArrayOfFunctions

Reputation: 3744

Why does compiler allow out-of-bounds array access even with constexpr index?

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

Answers (4)

Howard Hinnant
Howard Hinnant

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

Ryan Haining
Ryan Haining

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

David G
David G

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

Red Alert
Red Alert

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

Related Questions