理解复合键的 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<
,它比较三个数据成员的总和。
我了解如何使用 emplace
和 emplace_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);
我的问题是:
- 这是在没有提示的情况下调用
try_emplace
的唯一方法吗?如果不是,我怎么称呼它?
- 如何调用带有提示的
try_emplace
版本?我做了一些尝试,但总是失败。
- 假设
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。
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
- Is it correct to assume that try_emplace "move" or "copy" the CompositeKey inside the map?
无论您传递什么,都会转发给 try_emplace
实施。在上面的示例中,两个 CompositeKey
参数都是首先“自定义构造”的;然后,它们被传递给 try_emplace
,其中左值是复制构造的,而右值是移动构造的。
我有一个std::map<CompositeKey, std::string>
,其中CompositeKey
是我写的class。
这个 CompositeKey
有三个 int
数据成员,所有构造函数,所有复制赋值运算符和一个 friend bool operator<
,它比较三个数据成员的总和。
我了解如何使用 emplace
和 emplace_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);
我的问题是:
- 这是在没有提示的情况下调用
try_emplace
的唯一方法吗?如果不是,我怎么称呼它? - 如何调用带有提示的
try_emplace
版本?我做了一些尝试,但总是失败。 - 假设
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。
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
- Is it correct to assume that try_emplace "move" or "copy" the CompositeKey inside the map?
无论您传递什么,都会转发给 try_emplace
实施。在上面的示例中,两个 CompositeKey
参数都是首先“自定义构造”的;然后,它们被传递给 try_emplace
,其中左值是复制构造的,而右值是移动构造的。