tobi303 tobi303 - 23 days ago 8
C++ Question

Why std::vector::at() needs bounds checking even with optimizations turned on?

I have a problem with the

libstdc++-6.dll
in windows. This code:

#include <iostream>
#include <vector>
int main(){
std::vector<int> x(10);
std::cout << x.at(3) << std::endl;
}


compiles fine, but when I run it I get a error message saying


The procedure entry point _ZSt24__throw_out_of_range_fmtPKcz could not be located in the dll libstdc++-6.dll


My question is not how to fix this (it is most likely the wrong version of the dll and I just have to fix the PATH). However, this made me realize something quite unexpected:

The above code runs well (regardless of the wrong dll), when I turn on optimizations, ie

g++ error.cxx -O2


but this code

#include <vector>
#include <iostream>

double Foo(const std::vector<int>& x,int index) {
int m = x.at(index + 1) - x.at(index);
int b = x.at(index);
return b/(m*1.0);
}
int main(){}


does not.

Why do I get the above mentioned error with the second code no matter if I compile it via

g++ error.cxx -O2 or g++ error.cxx


?

Again, I know why the error appears, but I would expect that with optimizations turned on, both version do not cause the error. Instead the first version works fine while the second does not. Shouldn't
-O2
completely eliminate bounds checking?

Answer

The C++ standard requires bounds checking for at() and the actual implementation code for at() does contain the bounds checking.

In your first case however, the bounds are hardcoded (10 elements vs. index 3) and "everything" gets inlined with -O2, so the optimizer of the compiler removes the code for the bounds-check violation, because it can prove at compile time that the bounds are not violated and the code path not taken (as-if rule).

Therefore, you don't get a linker error with -O2 in that case, because the compiler simply didn't emit the call instruction at all.


Shouldn't -O2 completely eliminate bounds checking?

No, the optimizer has to keep to the AS-IF rule, that is, iff the optimizer can prove, at compile time, that a code path is not taken, it can eliminate that code. It doesn't just willy-nilly remove checks that were introduced in the source code.

As a side note, for vector::operator[] that doesn't require bounds checking, an implementation could (reasonably or not) introduce debug bounds checking, when e.g. NDEBUG is not defined, and only do no checking when NDEBUG is defined. In that case however, the bounds check would be "removed by the preprocessor" if you will, and not by the optimizer.