-1

I have a std::map<CompositeKey, std::string>, where CompositeKey is a class I wrote. This CompositeKey has three int data members, all the constructors, all the copy assignment operators and a friend bool operator<, which compares the sum of the three data members.

I understood how to use emplace and emplace_hint.

For example:

// 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));

The only way I was able to use try_emplace was in the version without hint:

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);

My questions are:

  1. Is this the only way to call try_emplace, without hint? If not, how could I call it?
  2. How could I call the try_emplace version with a hint? I made some attempts but always failed.
  3. Is it correct to assume that try_emplace "move" or "copy" the CompositeKey inside the map? I am asking about the behaviour of try_emplace both because I read something similar on another discussion, and because I wrote a verbose copy and move constructors to make a test.

I am sorry, I made my research but do not understand these points from the cppreference documentation

vaeVictis
  • 484
  • 1
  • 3
  • 13

1 Answers1

1

I think you may be misunderstanding the cppreference documentation. In your code, you are always trying to add a key that is not in the map. And you are passing that key as an lvalue reference. The only difference with your two cases is that you are using a hint in the second call. So the two try_emplace versions you are using, according to cppreference, are 1 and 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)

Notice:

  • Both versions receive the key as an lvalue reference (Key&).
  • First version returns a pair<iterator, bool, but second version just returns an iterator.

Now, the text below doesn't tell how you have to build your try_emplace arguments; instead, it says what try_emplace is doing internally.

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)...))

In short, just call try_emplace passing a CompositeKey object and a string as arguments (and, optionally, an iterator as a hint). The code below calls try_emplace with an lvalue and an rvalue (the second call with a hint), so it would use the versions 1 and 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?

Whatever you pass is forwarded to try_emplace implementation. In the example above, both CompositeKey arguments are first "custom constructed" ; then, they are passed to try_emplace, where the lvalue is copy constructed while the rvalue is move constructed.

rturrado
  • 7,699
  • 6
  • 42
  • 62
  • The first to calls are to ```emplace``` and to ```emplace_hint```. Only the third one is a call to ```try_emplace``` – vaeVictis Jan 10 '22 at 21:08
  • 1
    OK, I thought your problem was mainly with `try_emplace`. Anyway, whatever function you use, just pass `CompositeKey`s and strings are arguments. The only possible optimization I see is to pass `char*` instead of strings as rvalue arguments, so you save a string move. – rturrado Jan 10 '22 at 21:15
  • Yes, my problem is with ```try_emplace```. The first two calls behave as expected, i.e. they emplace the elements in the map. Give me the time to check your kind suggestion about ```try_emplace``` and I will give you a feedback. :) – vaeVictis Jan 10 '22 at 21:22
  • 1
    Perfect! I found the error in my code. I was calling ```try_emplace``` like you suggested... but I was still assigning its return value to ```std::tie(insertedIt, success)``` and not to just ```insertedIt```. The problem itself was not in the call, but in the fact that I was assigning the return value not properly. Many thanks again for your help! – vaeVictis Jan 10 '22 at 21:29
  • 1
    Oh sorry, yes, I forgot to comment that on my answer. I'll update it. I'm glad it's working now. – rturrado Jan 10 '22 at 21:44