I'm looking to remove one key - value element from an std::map and keep the value which was stored in this map. Just removing it is not enough, I need the key and value which was stored for other things.
My example is something like this:
std::map<const Class1 *, std::unique_ptr<Class 2>> myMap;
I would like to extract the key and value from the std::map. Simply moving it away is not an option, since this will put the std::map in an incorrect state.
I found the std::extract function, which I can use for an std::set, but not for an std::map. I cannot find any example online showing how I can extract the key and value from this map.
I would like to do something like this:
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.someMethod();
auto value = std::move(keyAndValue.someOtherMethod());
I thought I could use key() and value(), as described in some examples. But this does not work, I get an error.
error C2039: 'value': is not a member of
'std::_Node_handle<std::_Tree_node<std::pair<const
_Kty,_Ty>,std::_Default_allocator_traits<_Alloc>::void_pointer>,_Alloc,std::_Node_handle_map_base,_Kty,_Ty>'
You answered the question by yourself.
I just want to add a complete example. Please see:
#include <iostream>
#include <map>
#include <functional>
#include <memory>
struct Class1
{
void someMethod() const { std::cout << "Some Method from class 1\n";}
};
struct Class2
{
Class2(const int i) : test(i) {}
void someOtherMethod() const { std::cout << "Some other Method from class 2. Index: " << test << '\n';}
int test{0};
};
int main()
{
Class1 cl1_1{}, cl1_2{}, cl1_3{};
std::map<const Class1 *, std::unique_ptr<Class2>> myMap;
// Populate map
myMap[&cl1_1] = std::make_unique<Class2>(1);
myMap[&cl1_2] = std::make_unique<Class2>(2);
myMap[&cl1_3] = std::make_unique<Class2>(3);
// Extract data
auto keyAndValue = myMap.extract(myMap.find(&cl1_1));
// Get key and value
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
// Call methods
key->someMethod();
value->someOtherMethod();
return 0;
}
The answer was suggested by Mark.
For an std::map, I need to use mapped() on the node I get, not value.
Thanks a lot!
So, like this
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
Related
class TestingMapClass {
public:
TestingMapClass() : anotherStr("nothing") {}
void Load() {
std::unordered_map<std::string, std::string> memberMap = {
{"name", "value"}
};
// Do something with the unordered map...
anotherStr = memberMap["name"]; // The address of the local variable may escape the function
}
private:
std::string anotherStr;
};
int main() {
std::string testingStr;
{
std::unordered_map<std::string, std::string> someMap = {
{"one", "first"}
};
testingStr = someMap["one"];
}
std::cout << testingStr << std::endl; /// Local variable 'testingStr' may point to the memory which is out of scope
return 0;
}
I'm using CLion 2021.2.1 and I get these kind of 'warnings' here:
('The address of the local variable may escape the function'.
'Local variable 'testingStr' may point to the memory which is out of scope.')
From cppreference.com std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::operator[] indeed 'Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.'
But does it really matter actually? Don't anotherStr and testingStr both contain a copy of their respective unordered map's key's values (since the addresses of the strings and their respective unordered map's key's values seem to be different)?
What would be the key in case of Objects being inserted into std::set? As for the following example, I am inserting objects of class Classcomp into the std::set. However, I want to find whether an object of Classcomp with a particular 'id = 1' exists in the std::set or not?
#include <iostream>
#include <set>
struct Classcomp {
int val = 0;
int id = 0; ///ID for the object
bool operator() (const Classcomp& lhs, const Classcomp& rhs) const
{return lhs.val<rhs.val;}
};
int main ()
{
Classcomp c1,c2,c3,c4;
c1.val = 92;c1.id = 2;
c2.val = 94;c2.id = 3;
c3.val = 10;c3.id = 1;
std::set<Classcomp,Classcomp> fifth; // class as Compare
fifth.insert(c1);fifth.insert(c2);fifth.insert(c3);
for (auto x: fifth) {std::cout << x.id << " " << x.val << std::endl;}
if (fifth.find()) {std::cout << "Got it";} //What should I pass as arguments to fifth.find()?
return 0;
}
Sets is different from the map by exactly the fact that values of objects inside the set are their keys. If you need to separate a key from the value, you need a key-value container, such as std::map or std::unordered_map.
Clarification
I am not talking about the option of simply iterating over all objects in the set and checking every object to have specified key - which is easily done with std::find. If that's what you want, please clarify.
As your class is sorted by field val you have following options:
iterate over all elements and find one with particular id by std::find or std::find_if
create another container and sort there by id (maybe using std::share_ptr to avoid duplicates)
use boost::multiindex and create index by val as well as id
How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.
I have several GUIDs and I'd like to implement a hash table to quickly retrieve them. How would I do that?
If I treat the GUIDs as hash codes I need to do something like
index = GUID % prime_number_that_covers_all_GUID_bits
but I'm unsure if this is the right way to do it. How should I do to realize such a hash table?
You could use std::unordered_map, which takes a Key type (GUID) in your case, and a Value type, which could be some user info or program info (depending on your app). Storing is as simple as calling the member functions insert() or emplace() and looking up a stored value is done by calling find().
The example below uses std::string as the underlying type for your keys, and implicitly std::hash<std::string> as the hash function. For other GUID types, you might need to roll your own hash function object and pass that as a template parameter to the hash table.
#include <iostream>
#include <ios>
#include <string>
#include <unordered_map>
typedef std::string GUID;
class UserInfo
{
public:
UserInfo(bool b): is_genius_(b) {}
bool is_genius() const { return is_genius_; }
private:
bool is_genius_;
// your stuff here
};
int main()
{
std::unordered_map<GUID, UserInfo> table;
GUID x = "Johnny Pauling";
// insert into table
table.emplace(x, UserInfo(true));
// lookup in table
auto it = table.find(x);
// if found, print it
if (it != table.end())
std::cout << std::boolalpha << it->second.is_genius();
}
Output on LiveWorkSpace
I have a map named valueMap as follows:
typedef std::map<std::string, std::string>MAP;
MAP valueMap;
...
// Entering data.
Then I am passing this map to a function by reference:
void function(const MAP &map)
{
std::string value = map["string"];
// By doing so I am getting an error.
}
How can I get the value from the map, which is passed as a reference to a function?
std::map::operator[] is a non-const member function, and you have a const reference.
You either need to change the signature of function or do:
MAP::const_iterator pos = map.find("string");
if (pos == map.end()) {
//handle the error
} else {
std::string value = pos->second;
...
}
operator[] handles the error by adding a default-constructed value to the map and returning a reference to it. This is no use when all you have is a const reference, so you will need to do something different.
You could ignore the possibility and write string value = map.find("string")->second;, if your program logic somehow guarantees that "string" is already a key. The obvious problem is that if you're wrong then you get undefined behavior.
map.at("key") throws exception if missing key.
If k does not match the key of any element in the container, the
function throws an out_of_range exception.
http://www.cplusplus.com/reference/map/map/at/
The answer by Steve Jessop explains well, why you can't use std::map::operator[] on a const std::map. Gabe Rainbow's answer suggests a nice alternative. I'd just like to provide some example code on how to use map::at(). So, here is an enhanced example of your function():
void function(const MAP &map, const std::string &findMe) {
try {
const std::string& value = map.at(findMe);
std::cout << "Value of key \"" << findMe.c_str() << "\": " << value.c_str() << std::endl;
// TODO: Handle the element found.
}
catch (const std::out_of_range&) {
std::cout << "Key \"" << findMe.c_str() << "\" not found" << std::endl;
// TODO: Deal with the missing element.
}
}
And here is an example main() function:
int main() {
MAP valueMap;
valueMap["string"] = "abc";
function(valueMap, "string");
function(valueMap, "strong");
return 0;
}
Output:
Value of key "string": abc
Key "strong" not found
Code on Ideone
The main problem is that operator[] is used to insert and read a value into and from the map, so it cannot be const.
If the key does not exist, it will create a new entry with a default value in it, incrementing the size of the map, that will contain a new key with an empty string ,in this particular case, as a value if the key does not exist yet.
You should avoid operator[] when reading from a map and use, as was mention before, map.at(key) to ensure bound checking. This is one of the most common mistakes people often do with maps. You should use insert and at unless your code is aware of this fact. Check this talk about common bugs Curiously Recurring C++ Bugs at Facebook
How can I get the value from the map, which is passed as a reference to a function?
Well, you can pass it as a reference. The standard reference wrapper that is.
typedef std::map<std::string, std::string> MAP;
// create your map reference type
using map_ref_t = std::reference_wrapper<MAP>;
// use it
void function(map_ref_t map_r)
{
// get to the map from inside the
// std::reference_wrapper
// see the alternatives behind that link
MAP & the_map = map_r;
// take the value from the map
// by reference
auto & value_r = the_map["key"];
// change it, "in place"
value_r = "new!";
}
And the test.
void test_ref_to_map() {
MAP valueMap;
valueMap["key"] = "value";
// pass it by reference
function(valueMap);
// check that the value has changed
assert( "new!" == valueMap["key"] );
}
I think this is nice and simple. Enjoy ...
Although it's kinda late but I am still gonna answer, thanks to previous answers on this question i was able to forge this class which reuse pointers and values, it creates two maps to store data, Here the code if anybody interested..
template<class T1, class T2> class Bimap
{
std::map<T1, T2*> map1;
std::map<T2, T1*> map2;
public:
void addRow(T1 &t1, T2 &t2){
map1.insert(make_pair(t1, &t2));
map2.insert(make_pair(t2, &t1));
}
T2* findForward(T1 t1){
T2* value = map1.find(t1)->second;
return value;
}
T1* findBackward(T2 t2){
T1* value = map2.find(t2)->first;
return value;
}
};
Using class:
//Init mapp with int,int
Bimap<int,int> mapp;
//Add a row(Record) in bimap
int a = 5;
int b = 7002;
mapp.addRow(a, b);
//Print a record
int *ans= mapp.findForward(a);
cout<<"Bimap Returned:"<<*ans<<endl;