多个std :: map.insert()使用相同的std :: pair,但使用新值会导致错误的地图值。为什么以及如何解决?

问题描述 投票:0回答:1

多个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
###

reference c++17 stdmap std-pair
1个回答
0
投票

归结为:

struct S {
  int m;
  int& r = m;
};

S s1{42};
S s2{s1};
s1.m = 84;
std::cout << s2.r;  // prints 84

Demo

您具有一个包含“普通”成员和参考成员的结构。 (隐式定义的)默认构造函数以及聚合初始化将后者初始化为引用前者。您似乎假定后者已经或应该被初始化为始终引用前者always-事实并非如此。此结构还具有一个隐式定义的副本构造函数,该结构执行成员级副本-副本的非引用成员与原始对象的非引用成员具有相同的值,而副本的引用成员被初始化为引用与原始引用成员相同的对象。

在我的示例中,首先使用s1s1.m == 42引用s1.r来初始化s1.m。然后从中复制s2;结果是s2.r是指s1.m,而不是s2.m。然后修改s1.m,并忠实地反映s2.r的修改。

Struct1也发生了同样的事情。该结构将复制到地图中,副本中的引用成员以及向上引用的是原始(而非副本)中的非引用成员。然后,通过更新原始文件中的成员,可以有效地修改副本中的密钥,这时您的程序将显示未定义的行为。

© www.soinside.com 2019 - 2024. All rights reserved.