为什么编译器允许越界数组访问,即使使用 constexpr 索引也是如此
Why does compiler allow out-of-bounds array access even with constexpr index?
例如,如果我们有一个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
但是,这可以生成很多函数。大多数时候,如果你的设计是合理的,你就不需要这种类型的检查。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 数组索引的值没有增加
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- 查找最接近的大于当前数字的数字的索引
- Visual C++ constexpr Hints
- 在C++中调整向量中的索引
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 重载元组索引运算符-C++
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 为什么std::isnan 不是 constexpr?
- 是否可以将变体的索引作为 constexpr 变量获取?
- 从动态索引中选择ConstexPR索引
- 使用表达式模板编译时间数组索引--constexpr
- 为什么编译器允许越界数组访问,即使使用 constexpr 索引也是如此