为什么编译器允许越界数组访问,即使使用 constexpr 索引也是如此

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

本文关键字:constexpr 索引 访问 许越界 编译器 数组 越界 为什么      更新时间:2023-10-16

例如,如果我们有一个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 规范没有用 constexpr 标记std::array::operator[]const过载。

所以在 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被添加到 C++14 的const过载中。

更新

数组索引的 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 它始终可以在编译时检测越界。

对于常规 C 数组,std::array 上的数组访问是相同的,它从不检查索引是否有效,如果超出范围,它只会调用 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

但是,这可以生成很多函数。大多数时候,如果你的设计是合理的,你就不需要这种类型的检查。