为什么std :: vector :: at()即使启用了优化也需要边界检查?

for*_*818 2 c++ windows dll optimization indexoutofboundsexception

libstdc++-6.dll在Windows中有问题.这段代码:

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

编译好,但是当我运行它时,我收到一条错误消息说

程序入口点_ZSt24__throw_out_of_range_fmtPKcz无法位于DLL libstdc ++中 - 6.dll

我的问题不是如何解决这个问题(很可能是dll的错误版本,我只需要修复PATH).然而,这让我意识到一些非常意外的事情:

当我启用优化时,上面的代码运行良好(无论错误的dll如何),即

g++ error.cxx -O2
Run Code Online (Sandbox Code Playgroud)

但这段代码

#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(){}
Run Code Online (Sandbox Code Playgroud)

才不是.

为什么我用第二个代码得到上面提到的错误,无论我是通过编译它

g++ error.cxx -O2     or       g++ error.cxx 
Run Code Online (Sandbox Code Playgroud)

同样,我知道为什么会出现错误,但我希望在启用优化的情况下,两个版本都不会导致错误.相反,第一个版本工作正常,而第二个版本没有.不应该-O2完全消除边界检查?

Mar*_* Ba 10

C++标准要求边界检查,at()实际的实现代码at()确实包含边界检查.

但是在第一种情况下,边界是硬编码的(10个元素与索引3)并且"所有内容"都被内联-O2,因此编译器的优化会删除边界检查违规的代码,因为它可以在编译时证明不违反边界并且不采用代码路径(如果规则).

因此,在这种情况下,您不会收到链接器错误-O2,因为编译器根本不会发出调用指令.


不应该-O2完全消除边界检查?

不,优化器必须遵守AS-IF规则,也就是说,如果优化器在编译时可以证明没有采用代码路径,那么它可以消除该代码.它不仅会毫不犹豫地删除源代码中引入的检查.

作为一个侧面说明,为vector::operator[]的是要求边界检查,实现可能(合理与否)介绍调试边界检查,当如NDEBUG没有定义的,只有做任何检查时NDEBUG被定义.但是,在这种情况下,如果您愿意,那么边界检查将被"预处理器"删除,而不是由优化器.