reference_wrapper: Is this UB?

reference_wrapper: Is this UB?

本文关键字:this UB Is wrapper reference      更新时间:2024-09-24

我很好奇这段代码是否是UB。

https://wandbox.org/permlink/nU0iPLCrPXwQ7Kor

代码不会崩溃(在GCC和Clang中,无论是否进行优化(,这让我越来越困惑。。

#include <list>
#include <vector>
#include <iostream>
#include <utility>
#include <queue>
using namespace std;

int main() {
deque<int> l {1, 2, 3, 4, 5};
vector<reference_wrapper<int>> v (l.begin(), l.end());
// v now holds references to 1, 2, 3, 4, 5 in l
cout << v[3] << 'n'; // 4
l.pop_front();    // 1 popped
for (auto num : l) {
cout << num << ' '; // 2 3 4 5 
}
cout << 'n';    
cout << v[0] << 'n';  // I think this should crash because 1 is dangling reference
l.pop_front();  // 2 popped
for (auto num : l) {
cout << num << ' '; // 3 4 5
}
cout << 'n';    
cout << v[1] << 'n';  // I think it should crash but it still outputs 2
}

我很好奇这段代码是否是UB。

是的,由于使用了悬空引用,这显然是未定义的行为。

未定义的行为意味着任何1都可能发生,包括但不限于提供预期输出的程序。但是永远不要依赖(或根据未定义行为的程序的输出得出结论(。

所以您看到的输出是未定义行为的结果。但正如我所说,不要依赖于有UB的程序的输出。该程序可能导致分段错误。

使程序正确的第一步是删除UB然后并且只有到那时您才能开始对程序的输出进行推理。


1对于未定义行为的更准确的技术定义,请参阅此处,其中提到:对程序的行为没有限制

您的代码之所以没有崩溃,是因为您在deque的实现细节方面很幸运。

deque以512字节的块为单位分配内存。deque中的第一个和最后一个块可以分别在它们的前面或后面部分填充。指针标记这些位置。pop_front只是将指针向前移动一个元素,只要第一个块不是完全空的。

在您的示例中,完整的内容可以放在一个块中。因此,您只需继续引用仍然分配的内存,并且deque认为这些内存是空的。当然,任何诸如push_front或任何其他调整大小的操作都可能改变这一点。