Stealing resources from std::map's keys allowed?
You're doing undefined behavior, using const_cast
to modify a const
variable. Don't do that. The reason it's const
is because maps are sorted by their keys. So modifying a key in-place is breaking the underlying assumption the map is built on.
You should never use const_cast
to remove const
from a variable and modify that variable.
That being said, C++17 has the solution for your problem: std::map
's extract
function:
#include <map>
#include <string>
#include <vector>
#include <utility>
int main() {
std::vector<std::pair<std::string, double>> v;
std::map<std::string, double> m{{"aLongString", 1.0},
{"anotherLongString", 2.0}};
auto extracted_value = m.extract("aLongString");
v.emplace_back(std::make_pair(std::move(extracted_value.key()),
std::move(extracted_value.mapped())));
extracted_value = m.extract("anotherLongString");
v.emplace_back(std::make_pair(std::move(extracted_value.key()),
std::move(extracted_value.mapped())));
}
And don't using namespace std;
. :)
Your code attempts to modify const
objects, so it has undefined behavior, as druckermanly's answer correctly points out.
Some other answers (phinz's and Deuchie's) argue that the key must not be stored as a const
object because the node handle resulted from extracting nodes out of the map allow non-const
access to the key. This inference may seem plausible at first, but P0083R3, the paper that introduced the extract
functionalities), has a dedicated section on this topic that invalidates this argument:
Concerns
Several concerns have been raised about this design. We will address them here.
Undefined behavior
The most difficult part of this proposal from a theoretical perspective is the fact that the extracted element retains its const key type. This prevents moving out of it or changing it. To solve this, we have provided the key accessor function, which provides non-const access to the key in the element held by the node handle. This function requires implementation "magic" to ensure that it works correctly in the presence of compiler optimizations. One way to do this is with a union of
pair<const key_type, mapped_type>
andpair<key_type, mapped_type>
. The conversion between these can be effected safely using a technique similar to that used bystd::launder
on extraction and reinsertion.We do not feel that this poses any technical or philosophical problem. One of the reasons the Standard Library exists is to write non-portable and magical code that the client can’t write in portable C++ (e.g.
<atomic>
,<typeinfo>
,<type_traits>
, etc.). This is just another such example. All that is required of compiler vendors to implement this magic is that they not exploit undefined behavior in unions for optimization purposes—and currently compilers already promise this (to the extent that it is being taken advantage of here).This does impose a restriction on the client that, if these functions are used,
std::pair
cannot be specialized such thatpair<const key_type, mapped_type>
has a different layout thanpair<key_type, mapped_type>
. We feel the likelihood of anyone actually wanting to do this is effectively zero, and in the formal wording we restrict any specialization of these pairs.Note that the key member function is the only place where such tricks are necessary, and that no changes to the containers or pair are required.
(emphasis mine)