多个std :: map.insert()使用相同的std :: pair,但使用新值会导致地图值不正确。如何在不创建此行为的情况下使用单个结构和引用?
#include <iostream> // c++17 gcc 8.3.0-6 debian
#include <map>
#include <tuple>
using std::endl, std::cout, std::cerr;
struct Struct1 {
int s1_int1 {}, s1_int2 {};
std::string s1_str1 {};
struct Key {
decltype (s1_int1) & key_part1;
decltype (s1_int2) & key_part2;
} key { s1_int1, s1_int2 };
struct Value {
decltype (s1_str1) & value_part1;
} value { s1_str1 };
struct Compare {
bool operator()( Key const & lhs, Key const & rhs) const {
return std::tie( lhs.key_part1, lhs.key_part2 ) < std::tie( rhs.key_part1, rhs.key_part2 );
}
};
void print (std::string message) const {
cerr<<message<<">> s1_int1:"<< s1_int1<<", s1_int2:"<<s1_int2<<", s1_str1:"<<s1_str1<<endl;
}
};
void r_print( std::pair<Struct1::Key,Struct1::Value> const & pair, bool is_inserted ) {
cerr<<"is_inserted:"<<is_inserted<<", key.key_part1:"<<pair.first.key_part1<<", key.key_part2:"<<pair.first.key_part2<<", value.s1_str1:"<<pair.second.value_part1<<endl;
};
int main()
{
std::map<Struct1::Key, Struct1::Value, Struct1::Compare > my_map {};
Struct1 map_value1 {11,12,"s13"}, map_value2 = {21,22,"s23"};
map_value1.print("map_value :12");
auto const r1 = my_map.insert( {map_value1.key,map_value1.value} );
r_print( *r1.first, r1.second );
for (auto & [key,value]:my_map )
cerr<< "key.key_part1:"<<key.key_part1<<", key.key_part2:"<<key.key_part2<<", value.s1_str1:"<<value.value_part1<<endl;
map_value2.print("map_value2:22");
auto const r2 = my_map.insert( {map_value2.key,map_value2.value} );
r_print( *r2.first, r1.second );
for (auto & [key,value]:my_map )
cerr<< "key.key_part1:"<<key.key_part1<<", key.key_part2:"<<key.key_part2<<", value.s1_str1:"<<value.value_part1<<endl;
map_value1.s1_int1 = 31; map_value1.s1_int2 = 32; map_value1.s1_str1 = "s33";
map_value1.print("map_value :31");
auto const r3 = my_map.insert( {map_value1.key,map_value1.value} );
r_print( *r3.first, r1.second );
for (auto & [key,value]:my_map )
cerr<< "key.key_part1:"<<key.key_part1<<", key.key_part2:"<<key.key_part2<<", value.s1_str1:"<<value.value_part1<<endl;
cout << "###" << endl;
return 0;
}
在输出注释中,由于重新使用map_value1,因此如何构建地图,然后在最后一次插入时将其断开。我一定会误解std :: map或其他内容。
我想我部分理解了为什么会发生这种情况,因为地图显然没有复制。但是我在使用std :: vector.push_back()时没有观察到这种行为。当我阅读这些成员函数的cppreference条目时,我看不到这些描述如何将不同的行为告知我。我还假定c ++容器在创建容器元素时会提供类似的行为。
一些人可能会建议的一个简单答案是不使用Struct1 :: Key中的引用,但是我正在尝试使顶层数据成员既可以在结构中轻松访问,又可以轻松地创建用于插入的数据对。 std :: map。
我想我可以以某种方式强制复制,这是我试图通过使用引用来避免的。或为每个插入创建一个“新”变量,但这似乎是不必要的,如果操作不正确,可能会导致内存泄漏或指针悬空(我很容易犯一个错误)。
总体目标是创建类似于内存数据库的功能。
我感谢您提供的任何见解。
输出:
map_value :12>> s1_int1:11, s1_int2:12, s1_str1:s13
is_inserted:1, key.key_part1:11, key.key_part2:12, value.s1_str1:s13
key.key_part1:11, key.key_part2:12, value.s1_str1:s13
map_value2:22>> s1_int1:21, s1_int2:22, s1_str1:s23
is_inserted:1, key.key_part1:21, key.key_part2:22, value.s1_str1:s23
key.key_part1:11, key.key_part2:12, value.s1_str1:s13
key.key_part1:21, key.key_part2:22, value.s1_str1:s23
map_value :31>> s1_int1:31, s1_int2:32, s1_str1:s33
is_inserted:1, key.key_part1:31, key.key_part2:32, value.s1_str1:s33
key.key_part1:31, key.key_part2:32, value.s1_str1:s33
key.key_part1:21, key.key_part2:22, value.s1_str1:s23
key.key_part1:31, key.key_part2:32, value.s1_str1:s33
###
归结为:
struct S {
int m;
int& r = m;
};
S s1{42};
S s2{s1};
s1.m = 84;
std::cout << s2.r; // prints 84
您具有一个包含“普通”成员和参考成员的结构。 (隐式定义的)默认构造函数以及聚合初始化将后者初始化为引用前者。您似乎假定后者已经或应该被初始化为始终引用前者always-事实并非如此。此结构还具有一个隐式定义的副本构造函数,该结构执行成员级副本-副本的非引用成员与原始对象的非引用成员具有相同的值,而副本的引用成员被初始化为引用与原始引用成员相同的对象。
在我的示例中,首先使用s1
和s1.m == 42
引用s1.r
来初始化s1.m
。然后从中复制s2
;结果是s2.r
是指s1.m
,而不是s2.m
。然后修改s1.m
,并忠实地反映s2.r
的修改。
Struct1
也发生了同样的事情。该结构将复制到地图中,副本中的引用成员以及向上引用的是原始(而非副本)中的非引用成员。然后,通过更新原始文件中的成员,可以有效地修改副本中的密钥,这时您的程序将显示未定义的行为。