Accessing std::map keys and values
How do you access an s开发者_如何学运维td::vector
of the keys or values of an std::map
?
Thanks.
Edit: I would like to access the actual elements, not just copies of their contents. essentially I want a reference, not a copy.
This is essentially what I am wanting to do:
std::map<std::string, GLuint> textures_map;
// fill map
glGenTextures( textures_map.size(), &textures_map.access_values_somehow[0] );
You can't do that because neither the values nor the keys are laid out consecutively in memory. Each key/value pair is allocated independently in memory. In a situation like yours, you have to copy the values around.
There isn't a single function you can call to get all of the keys or values from an STL map. Instead, you can use the map iterators to access this information:
for (map<K, V>::iterator itr = myMap.begin(); itr != myMap.end(); ++itr) {
// Access key as itr->first
// Access value as itr->second
}
If you want to write a function that takes these values and wraps them up in an STL vector, then you could do so like this:
template <typename K, typename V>
std::vector<K> GetKeys(const std::map<K, V>& m) {
std::vector<K> result;
result.reserve(m.size()); // For efficiency
for (typename std::map<K, V>::const_iterator itr = m.begin(); itr != m.end(); ++itr)
result.push_back(itr->first);
return result;
}
template <typename K, typename V>
std::vector<V> GetValues(const std::map<K, V>& m) {
std::vector<V> result;
result.reserve(m.size()); // For efficiency
for (typename std::map<K, V>::const_iterator itr = m.begin(); itr != m.end(); ++itr)
result.push_back(itr->second);
return result;
}
Note that in these template functions, the type of the iterator is
typename std::map<K, V>::const_iterator
instead of
std::map<K, V>::const_iterator
This is because const_iterator
here is a dependent type - a type that depends on a template argument - and consequently for silly historical reasons must be prefaced by the typename
keyword. There's a good explanation of this here.
Hope this helps!
As others have said in their answers, you can't access all the keys or values in the form of a std::vector without copying them - std::map isn't designed for this form of access.
For your situation, I would set up 2 containers:
std::vector<GLuint> texture_id;
std::map<std::string, size_t> texture_map;
where the vector stores the ID, and the map's value is the index to the ID in the vector. When you add a new texture, add the ID into the vector with push_back()
, whose index is stored in a map entry with the last element's index, e.g.:
pair<map<string, size_t>::iterator, bool> result =
texture_map.insert(make_pair(new_texture_name, -1)); //ignore the index for now
if(result.second) { //true when the texture name isn't already in the map
texture_id.push_back(new_id);
result.first->second = texture_id.size()-1; //update the index in the map to this ID's element in texture_id
}
The push_back would maintain the same indexes for old IDs. Encapsulate all this within a class with functions to add and search for textures, just like in the answer to your other question.
This would let you call, after the IDs are loaded:
glGenTextures( textures_id.size(), &(textures_id[0]) );
... since std::vector guarantees that elements are consecutive to one another in memory.
edit: changed the map's value type; this was previously GLuint*, pointing at the vector's elements. Thanks to Oli Charlesworth for pointing out this design's flaw. edit: added sample code
Further to what others have said, it sounds very much like you really want to put the values into a std::vector
and then sort the vector; that way you can access the contents like you want to (i.e. via a pointer to a contiguous array of them).
Note that this assumes that reading will be your bottleneck, and that writing (i.e. setting up the vector) will be done relatively infrequently. If not, then you're probably better off sticking with the std::map
, and then copying the contents into a temporary array/vector every time you want to use them in this fashion.
精彩评论