理解复合键的 std::map try_emplace 行为的问题

Problem understanding the behaviour of std::map try_emplace for a composite key

我有一个std::map<CompositeKey, std::string>,其中CompositeKey是我写的class。 这个 CompositeKey 有三个 int 数据成员,所有构造函数,所有复制赋值运算符和一个 friend bool operator<,它比较三个数据成员的总和。

我了解如何使用 emplaceemplace_hint

例如:

// emplace
std::map<CompositeKey, std::string> my_map;
int first_id = 10, second_id = 100, third_id = 1000;
std::string my_string = "foo";
auto [ insertedIt, success ] = my_map.emplace(std::piecewise_construct,     
                                              std::forward_as_tuple(first_id, second_id, third_id), 
                                              std::forward_as_tuple(my_string));
// emplace_hint
first_id = 5, second_id = 50, third_id = 500;
my_string = "bar";
std::tie(insertedIt, success) = my_map.emplace_hint(insertedIt
                                                    std::piecewise_construct,     
                                                    std::forward_as_tuple(first_id, second_id, third_id), 
                                                    std::forward_as_tuple(my_string));

我能够使用 try_emplace 的唯一方法是在没有提示的版本中:

first_id = 1, second_id = 10, third_id = 100;
my_string = "foobar";
std::tie(insertedIt, success) = my_map.try_emplace({first_id, second_id, third_id},
                                                   my_string);

我的问题是:

  1. 这是在没有提示的情况下调用 try_emplace 的唯一方法吗?如果不是,我怎么称呼它?
  2. 如何调用带有提示的 try_emplace 版本?我做了一些尝试,但总是失败。
  3. 假设try_emplace“移动”或“复制”地图内的 CompositeKey 是否正确?我询问 try_emplace 的行为既是因为我在另一个讨论中读到类似的东西,也是因为我写了一个冗长的复制和移动构造函数来进行测试。

对不起,我做了研究但不明白cppreference documentation

中的这些要点

我认为您可能误解了 cppreference 文档。在您的代码中,您总是试图添加一个不在地图中的键。您将该键作为左值引用传递。这两种情况的唯一区别是您在第二次调用中使用了提示。因此,根据 cppreference,您使用的两个 try_emplace 版本是 1 和 3。

template< class... Args > pair<iterator, bool> try_emplace( const Key& k, Args&&... args ); (1)   (since C++17)

template< class... Args > iterator try_emplace( const_iterator hint, const Key& k, Args&&... args ); (3)  (since C++17)

通知:

  • 两个版本都接收密钥作为左值引用 (Key&)。
  • 第一个版本returns一个pair<iterator, bool,但是第二个版本只是returns一个iterator

现在,下面的文字没有说明您必须如何构建 try_emplace 论点;相反,它说明了 try_emplace 在内部做什么。

1) If a key equivalent to k already exists in the container, does nothing.
   Otherwise, behaves like emplace except that the element is constructed as

value_type(std::piecewise_construct,
           std::forward_as_tuple(k),
           std::forward_as_tuple(std::forward<Args>(args)...))

简而言之,只需调用 try_emplace 传递一个 CompositeKey 对象和一个字符串作为参数(以及可选的迭代器作为提示)。下面的代码使用左值和右值调用 try_emplace(第二次调用带有提示),因此它将使用版本 1 和 4。

[Demo]

std::map<CompositeKey, std::string> my_map;

std::cout << "Case 1:\n";
auto key1{CompositeKey{10, 100, 1000}};
auto value1{std::string{"foo"}};
auto [insertedIt, success] = my_map.try_emplace(key1, value1);

std::cout << "Case 2:\n";
insertedIt = my_map.try_emplace(insertedIt, {5, 50, 500}, "bar");

// Outputs:
//
//   Case 1:
//       custom ctor
//       copy ctor
//   Case 2:
//       custom ctor
//       move ctor
  1. Is it correct to assume that try_emplace "move" or "copy" the CompositeKey inside the map?

无论您传递什么,都会转发给 try_emplace 实施。在上面的示例中,两个 CompositeKey 参数都是首先“自定义构造”的;然后,它们被传递给 try_emplace,其中左值是复制构造的,而右值是移动构造的。