Thursday, December 25, 2025

Watch out for std::vector::at()

 Aspects of operator[] vs at() 

 In order to bump the default memory safety of C++ the committee has decided to harden the STL with adding bounds checking to operator[]. This is redundant since bounds checking is already present through function 'at()'. A safety profile could enforce use of 'at()' and issue a warning for the use of operator[].

 This hardening decision is not a free lunch and has consequences for performance. If we compare current operator[] which has no bounds checking with 'at()' with bounds checking it is 5 times slower in a test case. Below is the test case with two functions:

int g_iTemp = 0;

void PrfStlVectorIteratorIndex(const std::vector<int>& rv)
{
   int nTemp = 0;
   
   const size_t nLoop = rv.size();
   	
   for (size_t n = 0; n != nLoop; ++n)
   {
      nTemp += rv[n];
   }
   
   g_iTemp = nTemp;
}
   
void PrfStlVectorIteratorIndex(const std::vector<int>& rv)
{
   int nTemp = 0;
    
   const size_t nLoop = rv.size();
    
   for (size_t n = 0; n != nLoop; ++n1)
   {
      nTemp += rv.at(n);
   }
    
   g_iTemp = nTemp;
}

The results for a certain test with VS2022 17.14.23 with /O2: 

Function                  #            Total(s) 
PrfStlVectorIteratorIndex 1 0.149972
PrfStlVectorIteratorIndexAt 1 0.727781

The function using 'at()' is 5 times slower. A reason could be found when looking at the generated assembly. MSVC uses SIMD instructions in case of operator[] but it cannot use them with 'at()'.

Conclusion 

 This is a significant difference. It makes one wonder why the C++ committee took the decision so lightly to tax every invocation of operator[]. Especially since a major use case for operator[] is to use it in a loop as above where there is no danger of going out of bounds. The committee's argument is that it costed only 0.3% extra performance which clearly contradicts above numbers. Also they stated that on certain code bases it revealed thousand extra bugs. Not sure what that code base is. For decades we use Visual Studio with Microsoft's STL which has the extra checking turned on in debug mode and it never fires these asserts when using or testing debug builds. If it would fire it would reveal a bug and one can repair it. Let users who value safety over performance use the 'at()' variants but leave the operator[] alone.

 

Tuesday, December 23, 2025

Thoughts on C++ 26

Sutter's video

 The other day I watched Sutter's YouTube video about 3 cool things in C++ 26:

  1. Make C++ safer by replacing undefined behavior (UB) with erroneous behavior (EB)
  2. Reflection
  3. Yet another syntax for async

Safe C++ 

Sutter mentions two aspects:

  • uninitialized local variables will be data mangled. The compiler may inject code to check if uninitialized variables are accessed.
  • hardening of STL; most notably operator[] 
According to studies the overhead is minimal (0.3%). This number is debatable: they can never know what applications are out there. In the past we had bad experience with VS 2008 who turned on safe iterators in release builds. They killed all compiler optimizations right away when used.

I question also the first bullet: why not make it simpler and state that every variable will be default or zero initialized. There is no EB or UB necessary; or no hidden code injected by the compiler.

Some of the hardened STL functions are unnecessary. There are already 'at()' functions which bounds check. A safety profile could warn for use of operator[].

Reflection

Nice that reflection is added but I wonder if the C++ committee has the right priorities. The standard library even lacks a standard JSON or XML library which would be an ideal candidate for automatic serialization through reflection.

Async

They added a new superfluous new syntax. So much for consistency.

 Conclusion

Memory safety is an issue but I believe more in safety profiles than changing the language. Even so I would go then for zero initialization instead of checks with hidden costs. Reflection is nice but what C++ lacks most is standard libraries; not major language changes.

Careful with refactoring

Refactoring issue  Last year we applied a small refactoring in a piece of code. The construct was a parent - child relationship with the chi...