当索引不是整数常量表达式时,不要使用数组下标;使用 gsl::at() 代替

Do not use array subscript when the index is not an integer constant expression; use gsl::at() instead

我试图在 Microsoft Visual Studio 中创建一些示例代码,看起来像这样

int main()
{
    const size_t size = 10;
    int arr[size];

    for (size_t i = 0; i < size; ++i)
        arr[i] = i;

    return 0;
}

现在 JetBrains ResharperC++ 在 arr[i] = i;

行发出以下警告

Do not use array subscript when the index is not an integer constant expression; use gsl::at() instead

我不明白这意味着什么以及如何解决这个警告。

因为这是我经常使用的方案,所以我有点担心这个警告。

谁能给我建议或指出正确的方向?

编辑: 将循环更改为:

for (size_t i = 0; i < size; ++i)
    arr[i] = 0;

仍然产生警告。

我认为警告的原因是 operator[] 没有边界检查,而 gsl::at() 可以。

由于 size 在编译时已知,如果索引是 constexpr,您可能会收到警告,但如果该值是在运行时确定的,则不能。在我看来完全没有必要。

警告 arr[i] 不进行任何边界检查,您应该使用 https://github.com/Microsoft/GSL 中的 gsl::at(arr, i) 代替,因为它进行边界检查并且更安全。

按索引访问数组元素而不进行任何边界检查,这不是一个好的做法。这是一种不安全的直接内存访问,可能会导致 segmentation fault.

对于像 std::vector, there's this at() 成员函数这样的 STL 容器,它执行边界检查并且是访问元素的推荐方式。

对于这个简单的示例,您可以忽略此警告。但是,对于重要代码,请使用 std::vector。但是,对于 C 风格的数组,您可以下载并使用 gsl::at() 并探索它的其他功能。


参考文献:
C++ Core Guidelines
GSL (Guideline Support Library)

这不是(编译器)警告。它是 C++ Core Guidelines 并入第 3 方 IDE / 分析工具之一。

一般

for (size_t i = 0; i < size; ++i)
    arr[i] = something;

很危险。您无法判断 arr[i] 是否会超出数组范围。这就是 C++ Core Guidelines 建议您使用 gsl::at() 的原因,因为它会进行边界检查以确保您不会越界。

但这不是唯一的解决方案。如果您只需要在范围内迭代,您可以使用基于范围的 for 循环,例如

for (const auto& e : arr)
    //e is each element of the array and is not mutable here

for (auto& e : arr)
    //e is each element of the array and is mutable here

对于像您这样需要填充数组的情况,您可以使用 std::iota like

std::iota(std::begin(arr), std::end(arr), 0);

而且这些都保证不会越界

这是一个警告,因为 operator[] 不检查绑定,与 at 相反。 而在您的情况下,代码是正确的,"small" 更改可能会破坏您的循环 (int arr[2 * size];)

在您的情况下,有几个使用迭代器(显式或隐式)的好选择:

const size_t size = 10;
int arr[size];

std::iota(std::begin(arr), std::end(arr), 0);

int i = 0;
for (auto& e : arr) {
    e = i;
    ++i;
}

int array[] = {0, 1, 2};
for (auto element : array){
   if(element == 1){
   //.....
   }
}

试试上面的代码。

这似乎需要更新的答案。

建议使用标准徽标,而不是使用手写循环。在这种情况下 iota 完全满足需要:自动递增赋值。

我们还认为,静态数组应该使用 std::array 而不是 'C' 样式数组。但这对 vector 也同样有效,因为 iota 需要运行时参数。

template<size_t SZ>
auto<int, SZ> CountInit()
{
    std::array<int, SZ> arr; // Note we only use SZ once

    std::iota(arr.begin(), arr.end(), 0);
    return arr;
}

这可以使用 integer_sequence 和一些助手以更编译时间的方式完成:

// Note taken from : https://jgreitemann.github.io/2018/09/15/variadic-expansion-in-aggregate-initialization/
template <typename Container, int... I>
Container iota_impl(std::integer_sequence<int, I...>) {
    return {I...};
} 

template <typename T, std::size_t N>
auto iota_array() {
    using Sequence = std::make_integer_sequence<int, N>;
    return iota_impl<std::array<T, N>>(Sequence{});
}



auto foo2()
{
    return iota_array<int, 10>();
}

int goo2()
{
    auto x=foo2();
    return std::accumulate(x.begin(), x.end(), 0);
}

根据 https://godbolt.org/z/59hqqdEYj 这些是等效的(根据 c++20 iota 是 constexpr)