当 C++ 将元素从函数的 return 值存储到 std::vector 时出现意外结果

Unexpected result when C++ store element into std::vector from return value of function

当函数涉及重分配时,我发现有些编译器可能会保存函数调用前的地址。它导致存储在无效地址中的return值。

上面的描述中有一个例子来解释行为。

#include <stdio.h>
#include <vector> 
using namespace std;

vector<int> A; 
int func() { 
    A.push_back(3);
    A.push_back(4);
    return 5; 
} 
int main() { 
    A.reserve(2);
    A.push_back(0);
    A.push_back(1);
    A[1] = func();
    printf("%d\n", A[1]);
    return 0;
}

有一些常见的C++编译器,测试结果如下

这是未定义的行为吗?

此行为在 C++17 之前的所有 C++ 版本中均未定义。原因很简单,赋值运算符的两侧可以按任意顺序求值:

  • 假设首先对 A[1] 求值,你会得到一个 int& 指向 A 的第二个元素。
  • 然后,评估 func(),这可以重新分配向量的存储,留下先前检索的 int& 悬空引用。
  • 最后,执行分配,写入未分配的存储。由于标准分配器缓存内存,OS 通常不会捕获此错误。

仅在 C++17 中,special rule 20 进行了赋值:

In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

对于 C++17,A[1] 必须在调用 func() 之后进行计算,然后提供定义的、可靠的行为。

如果您检查 documentation,在 "Iterator Invalidation" 下,您会看到 push_back() 如果更改容量,可能会使 每个迭代器 无效,因为它必须重新分配内存。请记住,对于 std::vector,指针也是有效的迭代器。因为 push_back() 可能会也可能不会重新分配,而且您无法知道它是否会,所以行为是未定义的。