使用std::map是否应该是确定性的? 我正面临着一个奇怪的行为使用英特尔C++编译器2019更新5。当我填充std::map时,它似乎会导致一个不确定的(?)结果。stl来自VS2019 16.1.6,其中嵌入了ICC。我使用的是Windows 10.0.17134.286

使用std::map是否应该是确定性的? 我正面临着一个奇怪的行为使用英特尔C++编译器2019更新5。当我填充std::map时,它似乎会导致一个不确定的(?)结果。stl来自VS2019 16.1.6,其中嵌入了ICC。我使用的是Windows 10.0.17134.286,c++,stl,visual-studio-2019,icc,C++,Stl,Visual Studio 2019,Icc,我的代码: #include <map> #include <vector> #include <iostream> std::map<int, int> AddToMapWithDependencyBetweenElementsInLoop(const std::vector<int>& values) { std::map<int, int> myMap; for (int i = 0; i

我的代码:

#include <map>
#include <vector>
#include <iostream>

std::map<int, int> AddToMapWithDependencyBetweenElementsInLoop(const std::vector<int>& values)
{
    std::map<int, int>  myMap;
    for (int i = 0; i < values.size(); i+=3)
    {
        myMap.insert(std::make_pair(values[i], myMap.size()));
        myMap.insert(std::make_pair(values[i + 1], myMap.size()));
        myMap.insert(std::make_pair(values[i + 2], myMap.size()));
    }
    return myMap;
}

std::map<int, int> AddToMapOnePerLoop(const std::vector<int>& values)
{
    std::map<int, int>  myMap;
    for (int i = 0; i < values.size(); ++i)
    {
        myMap.insert(std::make_pair(values[i], 0));
    }
    return myMap;
}

int main()
{
    std::vector<int> values{ 6, 7,  15, 5,  4,  12, 13, 16, 11, 10, 9,  14, 0,  1,  2,  3,  8,  17 };

    {
        auto myMap = AddToMapWithDependencyBetweenElementsInLoop(values);
        for (const auto& keyValuePair : myMap)
        {
            std::cout << keyValuePair.first << ", ";
        }
        std::cout << std::endl;
    }

    {
        auto myMap = AddToMapOnePerLoop(values);
        for (const auto& keyValuePair : myMap)
        {
            std::cout << keyValuePair.first << ", ";
        }
        std::cout << std::endl;
    }

    return 0;
}
好奇。我期望有18个条目,得到15个和14个(取决于插入方法,请参见代码)

仍然好奇,现在我得到了17和14个条目,而不是18和18个

$ icl /nologo /Od mycode.cpp
$ mycode.exe
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
现在,没有优化,我得到了18/18,正如预期的那样

我的问题有两个:1)得到这样的结果是正常的吗?2)如果不是(我怀疑)我做错了什么?我认为对编译器的简单调用可以正确调用
std::map::insert()
函数

问题是否存在于(){}的


感谢您帮助我理解这个问题并找到解决方案

只是猜测(…;i+=3)

我看到您的用例中的项目数可以除以3,但无论如何,对于更一般的情况,我会修复代码中的一个错误:

{
  std::map<int, int>  myMap;
  for (int i = 0; (i + 2) < values.size(); i+=3)  // ignore the possibly incomplete last triplet
{
std::map myMap;
对于(inti=0;(i+2)

我知道这与您的问题没有直接关系,但此修复可能会触发编译器优化器中的某些内容以生成正确的代码。

我无法重现此情况,但在任何情况下,为了省心,您可以更简单地填充映射:

  for (auto i: values) {
    myMap[i] = 0;
  }
无需使用
myMap.insert(std::make_pair(key,value))
只需在映射中添加条目即可


否则,您的代码将生成预期的输出(0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17两次,序列显然已排序,因为这是有序映射)如果在Ubuntu下使用gcc 8.4.0进行编译。我怀疑这只是您使用的特定编译器的一个错误。向编译器开发人员报告该错误,以便他们能够修复该错误将是有益的。

从语法上讲,您的代码很好。我看不到可能存在未定义的行为(就你所做的而言,你没有进一步隐藏疯狂的黑客行为,比如重新定义大小/地图,修改标准标题等等)

但是:

因为像这样的代码行,我在旧编译器中遇到了循环优化器问题

for (int i = 0; i < values.size(); ++i)
for(int i=0;i
在混合有符号和无符号整数/数据类型范围的情况下,我怀疑您的英特尔编译器可能存在循环展开问题。这可能也是由于循环内部的相应问题和那里的下标运算符的使用。这里的典型基本问题:关于允许的寄存器使用的错误假设。您能用这里有严格的尺码用法吗

进一步想法:


如果要打印的“静态”预定义值是以非常动态的方式创建的,而不是以硬代码构造的方式创建的,那么您是否可以重现该问题?如果您无法重现,则至少可以排除许多潜在的原因。

我无法使用GCC、Clang或Stock VS 16.2.0重现。代码本身定义良好,因此它是VS或ICC错误。如果你用cl而不是icl编译代码,你会得到同样的结果吗?@NathanOliver,如果我用cl,我总是得到预期的0..17,无论我使用什么编译开关。所以你怀疑ICC错误?看起来是这样。我想知道它是否试图做某种向量化,这破坏了插入。我闻到了未定义的味道在代码中的某个地方定义了行为。具体在哪里?我不能告诉你。我可以告诉你的是,只有当
是3的倍数时,你的第一个函数才定义得很好。否则,它将溢出缓冲区。你似乎在这里这样做,所以在本例中你应该是可以的。但这是需要小心的。注意:它可能不是even在你发布的代码部分。你不是想说
i+2
?@v010dya,你说得对。谢谢。i+3 for (auto i: values) { myMap[i] = 0; }
for (int i = 0; i < values.size(); ++i)