'Emplacing a struct in unordered_map, reference issue
I have the following definition for boost unordered_map
typedef boost::unordered::unordered_map<std::String, CLIENT_STATE> CLIENT_MAP;
Where CLIENT_STATE is an struct defined as follow:
typedef struct{
unsigned char state;
/* socket fd of the client */
int fd;
/* File path requested by the client */
char file_path [255];
/* Current file offset */
unsigned long int offset;
} CLIENT_STATE;
When I try to add an element to CLIENT_MAP using emplace, the CLIENT_MAP creates a separate copy of the element, not related to the original definition. i.e. any modifications to the original element definition, won't do any changes to the element in the unordered_map.
I was told before, that using emplace won't clone the my original element, it will place it directly in the container check the link
So what's the best way to let the changes to the added element in the container affects the original definition and vice versa.
This is what I mean:
CLIENT_STATE new_client;
new_client.offset = 5;
client_map.emplace("Test", new_client);
new_client.offset = 10;
cout << client_map["Test"].offest;
The cout won't print 10, it will print 5.
Currently to solve this issue, after an element to the unordered_map I work on the returned std::pair and do the modification, but I think it is not efficient way to do that.
Solution 1:[1]
You don't want to use emplace. What you want is called shared pointer semantics. However, for future visitors to this question, it may be useful to explain how to correctly use emplace with an associative container.
As @gha.st correctly commented, emplace will call the constructor of std::pair<std::string, CLIENT_STATE>, which will make copies of the string and CLIENT_STATE objects.
Since C++11, std::pair has an extra constructor overload taking a first argument of tag type std::piecewise_construct_t which allows the members of the std::pair themselves to be emplace-constructed. Observe:
client_map.emplace(std::piecewise_construct,
std::forward_as_tuple("Test"),
std::forward_as_tuple(5));
Now emplace will call the tag-overloaded constructor of pair, which in turn will emplace-construct its two data members from the arguments in the forwarded tuples.
In this case, CLIENT_STATE is an aggregate type, so (before C++20) it doesn't have a constructor which we can call with an int like this. But the above is the general technique to use with typical C++ class types. The best we can do for an aggregate before C++20 is to move-construct it from a temporary:
client_map.emplace("Test", CLIENT_STATE{ 0, 0, "", 5 });
In this case, this is of no help because the members of CLIENT_STATE are not move-constructible, so a move is the same as a copy.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
