Warning C26446

Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).

C++ Core Guidelines: Bounds.4: Don't use standard-library functions and types that are not bounds-checked.

Remarks

The Bounds profile of the C++ Core Guidelines tries to eliminate unsafe manipulations of memory. It helps you avoid the use of raw pointers and unchecked operations. One way to perform uniform range-checked access to buffers is to use the gsl::at() utility from the Guidelines Support Library. It's also a good practice to rely on standard implementations of at() available in STL containers.

This rule helps to find places where potentially unchecked access is performed via calls to operator[]. In most cases, you can replace such calls by using gsl::at().

  • Access to arrays of known size is flagged when a non-constant index is used in a subscript operator. Constant indices are handled by C26483 STATIC_INDEX_OUT_OF_RANGE.
  • The logic to warn on overloaded operator[] calls is more complex:
    • If the index is non-integral, the call is ignored. This also handles indexing in standard maps, since parameters in such operators are passed by reference.
    • If the operator is marked as non-throwing (by using noexcept, throw(), or __declspec(nothrow)), the call is flagged. We assume that if the subscript operator never throws exceptions, it either doesn't perform range checks or these checks are obscure.
    • If the operator isn't marked as non-throwing, it may be flagged if it comes from an STL container that also defines a conventional at() member function. Such functions are detected by simple name matching.
    • The rule doesn't warn on calls to standard at() functions. These functions are safe; replacing them with gsl::at() wouldn't bring much value.
  • Indexing into std::basic_string_view<> is unsafe, so a warning is issued. Replace the standard string_view by using gsl::basic_string_span<>, which is always bounds-checked.
  • The implementation doesn't consider range checks that user code may have somewhere in loops or branches. Here, accuracy is traded for performance. In general, you can often replace explicit range checks by using more reliable iterators or more concise enhanced for-loops.

Example

This example demonstrates how the gsl::at function can replace an indexed reference:

// 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';
}