Lets say we have some pair like class:
class PairLike{
public:
string key;
int val;
PairLike(string Key, int Val) : key(Key), val(Val){};
//...other members
};
A few objects to go along with it:
PairLike p1("a",1);
PairLike p2("b",2);
PairLike p3("c",3);
PairLike p4("d",4);
Is there a way of automatically working with this object?
For example, something similar to:
std::map<PairLike> container = {p1,p2,p3};
container.insert(p4);
Instead of writing something like:
std::map<string, int> container = {{p1.key, p1.val}, {p2.key, p2.val}, ... }
container.insert({p4.key, p4.val})
I'm aware that using an std::set<PairLike> with a comparator using is_transparentcan achieve the result I'm looking for. However, I am curious if there is any way to approach this problem with a map.
You could provide a conversion operator for converting to std::pair:
class PairLike{
public:
// ...
operator std::pair<const std::string, int>() {
return {key, val};
}
};
And use it like:
std::map<string, int> container{p1,p2,p3};
container.insert(p4);
Edit: please see the #krisz's answer for viable alternative that I overlooked here.
That is not possible. std::pair is defined as part of the class definition, and cannot be altered to a custom class.
P.S. As noted by #UnholySheep, it's defined in the CPP standard:
namespace std {
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
class map {
public:
// types
using key_type = Key;
using mapped_type = T;
using value_type = pair<const Key, T>;
...
Related
I am using std::unordered_map with a custom equality comparator class like so:
class KeyCompare {
private:
HelperClass* helper;
public:
KeyCompare(HelperClass* helper): helper(helper) {}
bool operator()(const Key& key1, const Key& key2) const {
return helper->doStuff(key1, key2);
}
}
At some point in my code I initialize my map like this:
HelperClass helper;
std::unordered_map<Key, Value, Hasher, KeyCompare> map;
I would like to pass helper to the map such that KeyCompare objects are created with this helper. Is such a thing possible? I can use some global variable if absolutely necessary, but I would really like to avoid that.
Since your KeyCompare needs a helper it isn't default constructible. You must therefore supply an instance to the unordered_map when you construct it.
Example:
HelperClass helper;
std::unordered_map<Key, Value, Hasher, KeyCompare> map{
1, // bucket count
Hasher{}, // hasher instance
KeyCompare{&helper} // your KeyCompare with the helper
};
Demo
I was wondering if this was a valid way to assign a multidimensional map inside of a class method and if not how would I go about doing this.
template<typename T>
std::map<std::string, std::map<std::string, T>> MT;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
MT[name][node] = v;
}
Edit1: I compiled and it gave me many errors but I will just give a portion of the 1st.
error C3376: 'MonsterType::MT': only static data member templates are allowed
Edit2:
I tried creating a struct
template<typename T>
struct Wrapper
{
typedef std::map<std::string, std::map<std::string, T>> MT;
};
I then added this inside the class
template<typename T>
Wrapper<T>::MT mt;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
mt[name][node] = v;
}
Then got this error amongst many others.
warning C4346: 'MT': dependent name is not a type
The struct works outside the class without being multidimensional map, but I am unsure how to access it as multidimensional map.. just trying different things.
The idea is I want to store data of several objects data and index them by name, node and value.
Edit3:
So this is what I went with, and haven't gotten an error (just yet :p)
std::map<std::string, std::map<std::string, int>> MT;
void MonsterTemplate(std::string name, std::string node, int v) {
MT[name][node] = v;
}
Just updating this for anyone looking for something similar
So I figured out how to construct this.
template<typename T>
struct Test
{
std::map<std::string, std::map<std::string, std::map<size_t, std::map<std::string, T>>>> testmap;
void MonsterTemplate(std::string creatureName, std::string name, std::string node, T v) {
size_t i = testmap[creatureName][name].size();
testmap[creatureName][name][i][node] = v;
}
};
Test<std::string> str;
Since I needed various data types for T the structure worked much better than trying to find a way to assign a template to a static class, I am only using std::string as an example.
This can then be used inside of the class's method since str has a global scope.
str.MonsterTemplate(creatureName, name, node, value);
I want to implement a map, which maps a string to a generic vector.
I want to do this:
std::map<std::string, std::vector<class T> > myMap;
Assuming the proposed myMap had the following inserted into it, it could be used as such:
vector<int> intVec = myMap["ListOfInts"]; // Works because "ListOfInts" maps to a vector<int>
vector<string> stringVec = myMap["ListOfStrings"]; // Works because "ListOfInts" maps to a vector<string>
When I declare the map with the above syntax the compiler has a heart attack.
Can anybody make any suggestions? Or a better associate array option in C++ (suggest non-boost before boost).
Since you know the type you want when you are writing your code, I propose this approach (untested):
// base class for any kind of map
class BaseMap {
public:
virtual ~BaseMap() {}
};
// actual map of vector<T>
template<typename T>
class MapT : public BaseMap, public std::map<std::string, std::vector<T>> {};
class MultiMap {
public:
template<typename T>
std::vector<T>& get(const std::string& key) {
std::unique_ptr<BaseMap>& ptr = maps_[std::type_index(typeid(T))];
if (!ptr) ptr.reset(new MapT<T>());
return ptr->second[key];
}
private:
std::map<std::type_index, std::unique_ptr<BaseMap>> maps_;
}
int main() {
MultiMap map;
std::vector<int>& intVec = map.get<int>("ListOfInts");
std::vector<std::string>& stringVec = map.get<std::string>("ListOfStrings");
}
Maybe this could work for you:
template <typename T>
class MyMap {
std::map<std::string, std::vector<typename T> > map;
public:
/*...*/
};
As mattnewport said boost::variant is one option.
Or to support any types, use boost::any by explicit using any_cast.
Considering boost might be heavy weight, maybe you can reinvent the wheel and simplify it, so that is non boost any more? lol
With C++11, you can use using, it does exactly what you want:
#include <vector>
#include <map>
#include <string>
template<typename T> using mymap = std::map<std::string, std::vector<T>>;
int main()
{
mymap<int> intmap;
mymap<std::string> stringmap;
std::vector<int> intvec = intmap["test"];
std::vector<std::string> stringvec = stringmap["test"];
return 0;
}
Live Demo
Template arguments must be decided compile time. If you want each key of your map to correspond to a vector of a different type, it won't really work. You can get around it with a polymorphic container that returns a void* that you cast to the correct type, but I would suggest trying to find another way to do whatever you want to do with your map first.
I'd like to use a structure just like std::map but without ordering, I don't need ordering and my key is pretty huge, so it's "less than" comparision takes time.
So, I saw unordered_map but it has a hash template argument, so, how to use unordered_map without hashing? I'll really need to build my own container?
This question applies to std::set too.
EDIT
Some answers have suggested to create my own hash, but I can't do this, I should have specified it here. The key contains floating point data, so hashing it would be a real bad idea. I need to compare (std::equal_to) directly.
Create your own hash, it's easily done by composing the overloads of std::hash on the fields of your key.
The cppreference example (same as previous link) is quite good (even if you do not need the template stuff):
struct S
{
std::string first_name;
std::string last_name;
};
template <class T>
class MyHash;
template<>
class MyHash<S>
{
public:
std::size_t operator()(S const& s) const
{
std::size_t h1 = std::hash<std::string>()(s.first_name);
std::size_t h2 = std::hash<std::string>()(s.last_name);
return h1 ^ (h2 << 1);
}
};
After that you can use it in the std::unorderd_map:
std::unordered_map<S, Value, MyHash<S>> the_map;
By the way std::unordered_set also need a hash.
You need to spetialize hash object for your key before declaring your unordered_map.
namespace std
{
template <>
class hash<Key>
{
public:
size_t operator()(const Key &) const
{
// ... your hash function for Key object ...
}
};
}
std::unordered_map<Key, Value> myMap;
Example, if I want you use as a key pair:
namespace std
{
class hash<pair<string, int>>
{
public:
size_t operator()(const pair<string, int> &s) const
{
size_t h1 = hash<string>()(s.first);
size_t h2 = hash<int>()(s.second);
return h1 ^ (h2 << 1);
}
};
}
unordered_map<pair<string, int>, string> myMap;
In Python, there is a class called defaultdict which is essentially a dictionary that will construct elements on demand according to a function specified by the user at construction time..
Does a similar class already exists in C++, or would I have to create it myself by inheriting from map and overwriting the at method?
This is not directly an answer to your question, but if you want the same behavior as defaultdict for aggregation, you could use map.emplace to assign a default value if the key does not exist, and return an iterator to the new or existing item (which avoids a second lookup):
unordered_map<int, size_t> map = {{1, 1}, {2, 3}};
// later...
for (int i = 1; i < 4; i++) {
auto emplace_pair = map.emplace(i, 0);
emplace_pair.first->second += 1;
}
There's nothing in the standard library that would do exactly what you want, you'll have to provide such a class yourself.
However, please note that it's a bad idea to publically inherit from a standard library container (such as std::map); they are not designed for this, they don't have virtual functions and they don't have a virtual destructor. Consider this example to see why it's a bad idea:
template <class K, class V, class C, class A>
void foo(const std::map<K, V, C, A> &arg)
{
doSomething(arg.at(K()));
}
struct MyMap : std::map<int, int>
{
int at(int) { return 7; }
};
int main()
{
MyMap m;
foo(m); //this will call std::map::at, NOT MyMap::at
}
Instead, have your class store a std::map (or perhaps std::unordered_map, whichever is better for your implementation) by value. Or, if you think you could re-use a lot of the standard map's member functions and only override some, you could inherit from it non-publically and publish only the functions you need. Example:
template <
class Key,
class Value,
class Comparator = typename std::map<Key, Value>::key_compare,
class Allocator = typename std::map<Key, Value>::allocator_type
>
class DefaultDict : private std::map<Key, Value, Comparator, Allocator>
{
public:
// Publish the clear() function as is
using std::map<Key, Value, Comparator, Allocator>::clear;
// Provide my own at()
Value& at(const Key &key) {
return std::map<Key, Value, Comparator, Allocator>::operator[](key); //call the inherited function
}
// Etc.
};