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

时间:2014-12-19 00:33:02

标签: c++ arrays c++14 constexpr c++17

例如,如果我们有一个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

我们不能以某种方式限制这个吗?

4 个答案:

答案 0 :(得分:13)

为确保在编译时评估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[]的{​​{1}}重载。

所以在C ++ 11中,你运气不好。在C ++ 14中,您可以使它工作,但前提是constexpr和调用索引运算符的结果都声明为array

<强>澄清

用于数组索引的C ++ 11规范读取:

constexpr

用于数组索引的C ++ 14规范读取:

                reference operator[](size_type n);
          const_reference operator[](size_type n) const;

即。 reference operator[](size_type n); constexpr const_reference operator[](size_type n) const; 已添加到C ++ 14的constexpr重载中。

<强>更新

用于数组索引的C ++ 17规范读取:

const

循环现已完成。可以在编译时计算Universe。 ; - )

答案 1 :(得分:7)

如果您在编译时知道数组索引,则可以将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,它始终可以在编译时检测到越界。

答案 2 :(得分:6)

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"

答案 3 :(得分:2)

简单的回答,因为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

然而,这可以产生许多功能。大多数情况下,如果您的设计合理,您将不需要这种类型的检查。