c++ - 奇怪的 std::map 行为

标签 c++ visual-studio-2012 g++

下面的测试程序

#include <map>
#include <iostream>

using namespace std;

int main(int argc, char **argv)
{
    map<int,int> a;
    a[1]=a.size();
    for(map<int,int>::const_iterator it=a.begin(); it!=a.end(); ++it)
            cout << "first " << (*it).first << " second " << (*it).second << endl;
}

g++ 4.8.1(Ubuntu 12.04 LTS)上编译时会导致不同的输出:

g++ xxx.cpp 
./a.out 
first 1 second 1

在 Visual Studio 2012 (Windows 7) 上(标准 Win32 控制台应用程序项目):

ConsoleApplication1.exe
first 1 second 0

哪个编译器是正确的?我做错了吗?

最佳答案

这实际上是一个格式良好的程序,它有两个同样有效的执行路径,所以两个编译器都是正确的。

a[1] = a.size()

在这个表达式中,= 的两个操作数的求值是无序的。

§1.9/15 [intro.execution] Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

但是,函数调用不是交错的,所以对 operator[]size 的调用实际上是不确定顺序的,而不是无顺序的。

§1.9/15 [intro.execution] Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.

这意味着函数调用可能以两种顺序之一发生:

  1. operator[] 然后 size
  2. size 然后 operator[]

如果键不存在并且您使用该键调用 operator[],它将被添加到 map 中,从而更改 map 的大小。所以在第一种情况下,将添加键,检索大小(现在为 1),并将 1 分配给该键。在第二种情况下,将检索大小(即 0),添加键,并将 0 分配给该键。

请注意,这不是导致未定义行为的情况。当对同一标量对象的两次修改或一次修改和一次读取未排序时,会发生未定义的行为。

§1.9/15 [intro.execution] If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

在这种情况下,它们不是未排序的,而是不确定排序的。

所以我们所拥有的是两个同样有效的程序执行顺序。任何一种都可能发生,并且都给出有效的输出。这是未指定的行为

§1.3.25 [defns.unspecified]
unspecified behavior
behavior, for a well-formed program construct and correct data, that depends on the implementation


所以回答你的问题:

Which compiler is right?

两者都是。

Am I doing something wrong?

大概吧。您不太可能想要编写具有这样两个执行路径的代码。与未定义的行为不同,未指定的行为是可以的,因为它可以被解析为单个可观察的输出,但如果你可以避免它,那么一开始就不值得拥有它。相反,不要编写具有这种歧义的代码。根据您想要的正确路径,您可以执行以下任一操作:

auto size = a.size();
a[1] = size; // value is 0

或者:

a[1];
a[1] = a.size(); // value is 1

如果您希望结果为 1 并且您知道 key 尚不存在,您当然可以执行第一个代码,但分配 size + 1

关于c++ - 奇怪的 std::map 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21133717/

相关文章:

c++ - 为什么在 x64 平台上的 Win32 WindowProc 中获取 AccessViolation?

c++ - 编译 C++ 项目 VS2012 时出现错误 C4335

c++ - 错误 LNK2019 : unresolved external symbol referenced in function main

c++ - 在项目中只调用一次宏

linux - ubuntu 13.04 和 g++

c++ - 如何判断我的 struct tm 是否处于无效状态?

c++ - 以二维项数组作为数据成员并使用运算符+重载的多项式类

c++ - 在 Visual Studio 中向 Qt UIC 生成的文件添加自定义代码

c++ - Ubuntu 12.04 C++11 代码不工作

c++ - 按给定轴的角度对点进行排序?