警告 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[]
调用发出警告的逻辑更为复杂:- 如果索引为非整数,则忽略调用。 也处理标准映射中的索引,因为此类运算符中的参数是通过引用传递的。
- 如果使用
noexcept
、throw()
或__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';
}