警告 C26446

优先使用 gsl::at() 而非未选中的下标运算符(bounds.4)。

C++ Core Guidelines:Bounds.4:请勿使用未选中边界的标准库函数和类型

备注

C++ Core Guidelines 的边界配置文件尝试消除对内存的不安全操作。 这有助于避免使用原始指针和未选中的操作。 对缓冲区执行统一范围检查访问的一种方法是使用指南支持库中的 gsl::at() 实用工具。 依靠 STL 容器中可用的 at() 标准实现也是一种不错的做法。

此规则有助于查找通过调用 operator[] 执行可能未选中访问权限的位置。 在大多数情况下,你可以使用 gsl::at() 来替换此类调用。

  • 在下标运算符中使用非常量索引时,将标记对已知大小的数组的访问。 常量索引由 C26483 STATIC_INDEX_OUT_OF_RANGE 处理。
  • 对重载 operator[] 调用发出警告的逻辑更为复杂:
    • 如果索引为非整数,则忽略调用。 也处理标准映射中的索引,因为此类运算符中的参数是通过引用传递的。
    • 如果使用 noexceptthrow()__declspec(nothrow),将运算符标记为非引发,则会标记调用。 假设下标运算符从不引发异常,则要么不执行范围检查,要么执行模糊检查。
    • 运算符未标记为非引发的情况下,如果运算符来自还定义常规 at() 成员函数的 STL 容器,则可能会标记该运算符。 通过简单的名称匹配来检测此类函数。
    • 在调用标准 at() 函数时,该规则不会对此发出警告。 这些函数是安全的;把它们替换为 gsl::at() 没有太大意义。
  • std::basic_string_view<> 的索引不安全,因此会发出警告。 使用 gsl::basic_string_span<> 来替换标准 string_view,会始终对其进行边界检查。
  • 此实现不考虑对用户代码在循环或分支中的某个位置进行范围检查。 在这里,准确性的代价是性能下降。 通常,可以使用更可靠的枚举器或更简洁的增强型 for循环来替换显式范围检查。

示例

此示例演示如何使用函数 gsl::at 替换索引引用:

// C26446.cpp
#include <vector>
#include <gsl/gsl_util>
#include <iostream>

void fn()
{
    std::vector<int> v{1, 2, 3, 4, 5};
  
    // Normal bracket operators do not prevent you from accessing memory out of bounds.
    std::cout << v[5] << '\n';  // C26446, prefer using gsl::at instead of using operator[].
  
    // gsl::at prevents accessing memory out of bounds and invokes std::terminate on access.
    std::cout << gsl::at(v, 5) << '\n';
}