我创建了一个类型的地图map<T, const T&>.对于当前的示例目的,让我们说T:
class Bar {
public:
Bar(int x) {this->x = x;}
int x;
};
Run Code Online (Sandbox Code Playgroud)
接下来,我创建一个地图并插入Bar一些整数键.
Bar bs[] = {Bar(1), Bar(2), Bar(3)};
map<int, const Bar&> my_map;
for (int i = 0; i < 3; i++) {
const Bar &b = bs[i];
cout << "Setting map." << i
<< " with x = " << b.x << endl ;
my_map.insert(std::make_pair(i, b));
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切看起来都很好,并按预期b.x打印值1; 2; 3.接下来,我们将检索这些值.
for (int i = 0; i < 3; i++) {
auto iter = my_map.find(i);
if (iter == my_map.end()) {
cout << "Not found!" << endl;
continue;
}
cout << "map." << i << " = " << iter->second.x << endl;
}
Run Code Online (Sandbox Code Playgroud)
输出每次打印最后一个值,如下所示.
// map.0 = 3
// map.1 = 3
// map.2 = 3
Run Code Online (Sandbox Code Playgroud)
正如我所料,这对我来说是令人困惑的1; 2; 3.如果我用const Bar它给出的替换值的地图类型1; 2; 3.我一直试图理解它,但到目前为止,它看起来像我未定义的行为.我可以想像的是,最疯狂的解释&b是像一个盒存储指针的对象,和盒结束跨越环路被共享,并且make_pair使用&b作为一箱值不是像指针/参考(并且因此解释了被印刷的最后一个值) .
编辑:我理解使用这样的地图可能不是一个好主意,但我很好奇为什么会发生这种情况而不是我应该使用的.从语义上讲,当我写这篇文章时,我错过了什么以及为什么它通过编译器,或者为什么编译器做出了任何假设.
编辑:运行代码的repl.it示例:https://repl.it/repls/IgnorantExhaustedBluejay
基本上与此处相同的问题:如何在向量内部引用一对?
您调用std::make_pair创建一个std::pair没有引用作为其第二个成员的临时对象.该second对的成员是类型的常规值Bar.同时,您的地图存储引用.引用绑定到由second创建的临时成员std::make_pair.后来临时被摧毁了.引用变得晃来晃去.
循环的每次迭代中的每个临时值显然都是在内存中的相同位置创建的.因此,地图中的所有这些悬空引用都指向内存中的相同位置.恰好3在打印时保留剩余值.这解释了输出.
带有原始引用的地图不是一个好主意.但是如果你想以某种方式强制它使用原始引用,请停止使用std::make_pair.相反,手动构造一个正确的std::pair,确保明确指定正确的类型
my_map.insert(std::pair<const int, const Bar &b>(i, b));
Run Code Online (Sandbox Code Playgroud)
或者您可以std::make_pair按如下方式继续使用
my_map.insert(std::make_pair(i, std::cref(b)));
Run Code Online (Sandbox Code Playgroud)
但完全切换到std::reference_wrapper并且std::cref是一个更好的主意.
PS BTW,在C++ 17模式下,GCC拒绝使用原始引用编译代码.C++ 14模式确实编译它.
| 归档时间: |
|
| 查看次数: |
73 次 |
| 最近记录: |