谁在这里? g++ 还是 Visual Studio 2017?

Who's right here? g++ or Visual Studio 2017?

最近看到一个有趣的"feature"。 下面的代码在 g++ 和 Visual Studio 2017.

上同样编译
#include <iostream>
#include <list>

int main()
{
    std::list<int *> l;
    int a = 1, b = 2;
    l.emplace_back(&a);
    auto p = l.front();
    std::cout << p << '\n'; // prints x
    l.erase(l.begin());
    l.emplace_back(&b);
    std::cout << p << '\n'; // prints x
    std::cin.get();
}

但是,如果你换行

auto p = l.front();

auto & p = l.front();

Visual Studio 仍然给出相同的输出(当然,考虑到地址 x 可能会改变)。但是,现在 g++ 给我输出

x
x+4

显然,当通过引用传递指针时,g++ 认识到列表的第一个元素现在具有不同的值,这是堆栈的不同地址(与初始地址相比偏移 + 4),而 Visual Studio 2017年没有。那么...谁坏了?

l.erase(l.begin()); 之后,对先前在 auto & p = l.front(); 获得的第一项的引用变得无效,并且访问存储在 p 中的值会导致未定义的行为。所以是你的代码坏了。

who's broken?

两者都是正确的,因为您的代码有未定义的行为。

auto & p = l.front(); 之后,你 erased 来自 list 的元素,然后 p 变成悬挂;对它的任何取消引用都会导致 UB,意味着一切皆有可能。

References and iterators to the erased elements are invalidated.