为什么使用空字符串流会产生依赖于编译器优化的结果

Why are there compiler-optimization dependent results using an empty stringstream

我对以下代码有点困惑,因为它在使用 g++ -O3 和 g++ -O1 时表现不同。

#include <iostream>
#include <set>
#include <fstream>
#include <string>
#include <map>
#include <sstream>

int main(int argc, char** argv){
    if(argc<=1){
        std::cerr << "No Input File... Abort" << std::endl;
        return 1;
    }
    std::ifstream in_file;
    in_file.open(argv[1]);
    if(!in_file) {
        std::cerr << "Input \"" << argv[1] << "\" Could not be opened!... Abort" << std::endl;
        return 1;
    }

    std::map<
        std::pair<int,int> /*position,level*/ ,
        int 
    > triangle;

    int level=0; //Counter of current depth in the triangle
    while(!in_file.eof()){
        std::string line;
        std::getline(in_file, line); //Read in complete line (level of triangle)
        std::cout << line << std::endl; //Print what he read
        std::istringstream iss (line); //Split line into pieces
        for(int position=-level;position<=level;position+=2){ //Move through one level of the triangle
            int value;
            iss >> value;
            std::pair<int,int> current_position(position,level); //Position in triangle
            triangle.emplace(current_position, value); //Erzeugung des Punktes und Speicherung des Pointers in der Map
        }
        level++;
    }
    // Print out map contents
    for(int i=0;i<level;++i){
        for(int position=-i;position<=i;position+=2){
            std::pair<int,int> current_position(position,i);
            std::cout << triangle.at(current_position) << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}

这个最小的例子只能读入一个文本文件,例如以下类型 末尾有一个空行 :

1
2 3

我知道如果文件末尾有一个空行,循环中的字符串流将是空的,因此流是垃圾。但是,我不明白为什么使用 -O3 或 -O1 时行为会有所不同:

g++ test.cpp -Wall -pthread -pedantic -Wextra -O1 -std=c++11 -o test
./test test_file 
1
2 3

1 
2 3 
3 3 3 
g++ test.cpp -Wall -pthread -pedantic -Wextra -O3 -std=c++11 -o test
./test test_file 
1
2 3

1 
2 3 
0 0 0 

这是在我的系统上测试的: gcc 版本 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)

如您所见,-O3 编译版本似乎导致流忘记了最后一次输入,而 -O1 编译版本似乎存储了最后读入的值 3,尽管它应该在下一个循环迭代。

每当您的代码行为因优化级别不同而不同时,这是代码表现出 未定义行为 的最强烈提示(另一个选项是优化器中的错误,它确实存在,但可能性要小得多)。

原因是优化级别越低,编译器就越有可能将 C++ 代码机械地翻译成 ASM 命令序列——与编写的完全一样。然而,随着优化级别的提高,编译器变得富有创造力,并开始对代码做出假设 - 思考它的方式是编译器 'believes' 代码永远不会表现出未定义的行为,因此省略了任何可能会出现的代码仅在存在未定义行为时执行。