Say I define a map with a custom comparator such as
struct Obj
{
int id;
std::string data;
std::vector<std::string> moreData;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; };
}
std::map<Obj,int,Comparator> compMap;
is there a good way to ensure that downstream users don't have to implement the comparator to use the map as a map?
for instance my compiler throws an error if I try to pass it to a function with a similar type.
template<class T>
inline void add(std::map<T, int>& theMap, T const & keyObj)
{
auto IT = theMap.find(keyObj);
if (IT != theMap.end())
IT->second++;
else
theMap[keyObj] = 1;
}
add(compMap,newObj); //type error here
EDIT:
I kinda over santitized this to make a generic case. and then overlooked the obvious
template<class T, class Comp, class Alloc>
inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
still having issues with one use not being able to deduce T, but went from 80 erros to 1 so... progress
thanks everyone.
You can typedef the specialised type and use that type inplace of
std::map<...
typedef std::map<Obj,int,Comparator> compMap_t;
inline void add(compMap_t& theMap, Obj const & keyObj)
...
Downstream users either use the type declared by you
using my_important_map = std::map<Obj,int,Comparator>;
or better use functions which take a generic map type,
auto some_function(auto const& map_)
{
//do something with the map and don't care about the ordering
return map_.find(Obj(1));
}
I have this class template which contains a map as following:
template<class K, class V>
class interval_map {
private:
std::map<K,V> m_map;
}
And I want to have a function that adds values to the maps and checks whether the key already exists or not so I am trying to do this using iterator :
void add_elements_test2 ( K const& key,V const& val)
{
std::make_pair<typename std::map<K,V>::iterator,bool>x;
x= m_map.insert(std::make_pair(key,val));
if(x.second = false)
{
cout<<"Key alreads exists "<<endl;
}
}
but I get this error when I create the operator:
std::make_pair<typename std::map<K,V>::iterator,bool>x;
Is this is a correct way?
std::make_pair<typename std::map<K,V>::iterator,bool>x;
It not correct the correct way to declare a std::pair. If you want to declare a std::pair for the return of insert then you need
std::pair<typename std::map<K,V>::iterator,bool> x;
And now x has the correct type. std::make_pair is a function that is used to construct a std::pair and you pass it the variables to make the pair from.
Instead of having to type all this though you can simply use auto like
auto x = m_map.insert(std::make_pair(key,val));
//...
Now x has the correct type and you do a lot less typing.
You also have a typo in
if(x.second = false)
In the above you are doing assignment, not comparison. Since you are setting the value to false the if statement will never run as it will always evaluate to false. You need
if(x.second == false)
Simply use auto:
auto x = m_map.insert(std::make_pair(key, val));
if (!x.second)
{
cout << "Key already exists" << endl;
}
Note: the type you want is pair
std::pair<typename std::map<K, V>::iterator, bool>
std::make_pair is an utility function to create std::pair.
I wrote this answer so that you don't hate template classes in the future. There are 2 scenarios you need to consider, and I have listed them both in the code below. Let me know if you have any questions, the comments are detailed.
Scenario 1, add_elements_test2 is defined inside the class
template<class K, class V>
class interval_map {
private:
std::map<K,V> m_map;
// This is fine because the K and V types are in the same scope for the map, and add_elements_test2
void add_elements_test2 ( K const& key,V const& val)
{
// Use auto here to simplify your life a little
auto x = m_map.insert(std::make_pair(key,val)); // actual type is std::pair<K, bool>
if(x.second == false)
{
cout<<"Key already exists "<<endl;
}
}
};
Scenario 2, add_elements_test2 is defined outside the class
template<class K, class V>
class interval_map {
private:
std::map<K,V> m_map;
void add_elements_test2 ( K const& key,V const& val);
};
// need another template
template<class K, class V>
// need to template interval_map, this could cause headaches if you did not realize this is a templated class
void interval_map<K, V>::add_elements_test2 ( K const& key,V const& val)
{
// Use auto here to simplify your life a little
auto x = m_map.insert(std::make_pair(key,val)); // actual type is std::pair<K, bool>
if(x.second == false)
{
cout<<"Key already exists "<<endl;
}
}
Essentially, your mistake with x is the type definition.
// pair is in the utility header
std::pair<K, bool> x= m_map.insert(std::make_pair(key,val));
I have a POD with about 30 members of various types and I will be wanting to store thousands of the PODs in a container, and then sort that container by one of those members.
For example:
struct Person{
int idNumber;
....many other members
}
Thousands of Person objects which I want to sort by idNumber or by any other member I choose to sort by.
I've been researching this for a while today and it seems the most efficient, or at least, simplest, solution to this is not use struct at all, and rather use tuple for which I can pass an index number to a custom comparison functor for use in std::sort. (An example on this page shows one way to implement this type of sort easily, but does so on a single member of a struct which would make templating this not so easy since you must refer to the member by name, rather than by index which the tuple provides.)
My two-part question on this approach is 1) Is it acceptable for a tuple to be fairly large, with dozens of members? and 2) Is there an equally elegant solution for continuing to use struct instead of tuple for this?
You can make a comparator that stores a pointer to member internaly so it knows which member to take for comparison:
struct POD {
int i;
char c;
float f;
long l;
double d;
short s;
};
template<typename C, typename T>
struct Comp {
explicit Comp(T C::* p) : ptr(p) {}
bool operator()(const POD& p1, const POD& p2) const
{
return p1.*ptr < p2.*ptr;
}
private:
T C::* ptr;
};
// helper function to make a comparator easily
template<typename C, typename T>
Comp<C,T> make_comp( T C::* p)
{
return Comp<C,T>(p);
}
int main()
{
std::vector<POD> v;
std::sort(v.begin(), v.end(), make_comp(&POD::i));
std::sort(v.begin(), v.end(), make_comp(&POD::d));
// etc...
}
To further generalize this, make make_comp take a custom comparator, so you can have greater-than and other comparisons.
1) Is it acceptable for a tuple to be fairly large, with dozens of members?
Yes it is acceptable. However it won't be easy to maintain since all you'll have to work with is an index within the tuple, which is very akin to a magic number. The best you could get is reintroduce a name-to-index mapping using an enum which is hardly maintainable either.
2) Is there an equally elegant solution for continuing to use struct instead of tuple for this?
You can easily write a template function to access a specific struct member (to be fair, I didn't put much effort into it, it's more a proof of concept than anything else so that you get an idea how it can be done):
template<typename T, typename R, R T::* M>
R get_member(T& o) {
return o.*M;
}
struct Foo {
int i;
bool j;
float k;
};
int main() {
Foo f = { 3, true, 3.14 };
std::cout << get_member<Foo, float, &Foo::k>(f) << std::endl;
return 0;
}
From there, it's just as easy to write a generic comparator which you can use at your leisure (I'll leave it to you as an exercise). This way you can still refer to your members by name, yet you don't need to write a separate comparator for each member.
You could use a template to extract the sort key:
struct A
{
std::string name;
int a, b;
};
template<class Struct, typename T, T Struct::*Member>
struct compare_member
{
bool operator()(const Struct& lh, const Struct& rh)
{
return lh.*Member < rh.*Member;
}
};
int main()
{
std::vector<A> values;
std::sort(begin(values), end(values), compare_member<A, int, &A::a>());
}
Maybe you want to have a look at boost::multi_index_container which is a very powerful container if you want to index (sort) object by different keys.
Create a class which can use a pointer to a Person member data to use for comparison:
std::sort(container.begin(), container.end(), Compare(&Person::idNumber));
Where Compare is:
template<typename PointerToMemberData>
struct Compare {
Compare(PointerToMemberData pointerToMemberData) :
pointerToMemberData(pointerToMemberData) {
}
template<typename Type
bool operator()(Type lhs, Type rhs) {
return lhs.*pointerToMemberData < rhs.*pointerToMemberData
}
PointerToMemberData pointerToMemberData;
};
I have a template class representing an array of numerical values.
I want this class to work for any type of numerical value (e.g. int, double, etc.) and three types of container (std::vector, std::deque, and std::list).
Here are the relevant bits of the implementation for my specific problem :
template < typename Numeric_t, typename Container = std::vector<Numeric_t> >
class Array {
// field member
Container m_data;
// other stuff here
// ...
// random element access for std::vector and std::deque
Numeric_t & operator[] (unsigned int index) { return m_data[index]; }
// random element access for std::list
Numeric_t & operator [] (unsigned int index) {
std::list<Numeric_t> :: iterator it = m_data.begin();
std::advance(it, index);
return *it;
}
}
Of course, the compiler doesn't allow me to overload the operator [].
What I would need is a kind of partial specialization for operator [] specific for std::list, but partial template function specialization is not allowed either in C++.
(I know that random element access is not efficient for a list, but that's not the point here).
Ideally, in the client code I would like to use the Array class like this :
Array < int, std::vector<int> > vec;
Array < int, std::list<int> > lst;
// fill arrays here
// ...
std::cout << vec[0] << std::endl;
std::cout << lst[0] << std::endl;
After lot of research I was not able to find a working solution.
What would be the most elegant way to solve this problem ?
Thanks for your help.
A clean solution is to use full-class template specialization. The different specializations can be derived form one common base class, in order to share common code.
Write a class ArrayBase containing all the code that does not depend on the particular container type and that grants access to the container, by making it protected or making Array a friend class.
template <class Numeric_t, class Container>
class Array
: public ArrayBase<Numeric_t, Container>
{
// Container specific code, generic version that works for all containers.
};
template <class Numeric_t>
class Array<Numeric_t, std::vector<Numeric_t>>
: public ArrayBase<Numeric_t, std::vector<Numeric_t>>
{
// Optimized code for std::vector.
}
Another approach: You can also write a static member function containing code to access the idx-th entry of the container and specialize that function:
template <class Numeric_t, class Container>
class Array
{
template <class Cont>
static Numeric_t get(Cont const& container, unsigned int idx)
{
std::list<Numeric_t>::iterator it = container.begin();
std::advance(it, idx);
return *it;
}
template <>
static Numeric_t get(std::vector<Numeric_t> const& container, unsigned int idx)
{
return container[idx];
}
public:
Numeric_t operator[](unsigned int idx) const { return get(m_data, idx); }
};
I am sorry, this does not work. I forgot that you can't specialize static member functions ... again.
Another alternative is to use SFINAE, but it is a non-idiomatic use of it and I would not recommend it in this case.
Is there a way to specify the default value std::map's operator[] returns when an key does not exist?
While this does not exactly answer the question, I have circumvented the problem with code like this:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
No, there isn't. The simplest solution is to write your own free template function to do this. Something like:
#include <string>
#include <map>
using namespace std;
template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}
int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}
C++11 Update
Purpose: Account for generic associative containers, as well as optional comparator and allocator parameters.
template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}
C++17 provides try_emplace which does exactly this. It takes a key and an argument list for the value constructor and returns a pair: an iterator and a bool.: http://en.cppreference.com/w/cpp/container/map/try_emplace
The C++ standard (23.3.1.2) specifies that the newly inserted value is default constructed, so map itself doesn't provide a way of doing it. Your choices are:
Give the value type a default constructor that initialises it to the value you want, or
Wrap the map in your own class that provides a default value and implements operator[] to insert that default.
The value is initialized using the default constructor, as the other answers say. However, it is useful to add that in case of simple types (integral types such as int, float, pointer or POD (plan old data) types), the values are zero-initialized (or zeroed by value-initialization (which is effectively the same thing), depending on which version of C++ is used).
Anyway, the bottomline is, that maps with simple types will zero-initialize the new items automatically. So in some cases, there is no need to worry about explicitly specifying the default initial value.
std::map<int, char*> map;
typedef char *P;
char *p = map[123],
*p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0
See Do the parentheses after the type name make a difference with new? for more details on the matter.
There is no way to specify the default value - it is always value constructed by the default (zero parameter constructor).
In fact operator[] probably does more than you expect as if a value does not exist for the given key in the map it will insert a new one with the value from the default constructor.
template<typename T, T X>
struct Default {
Default () : val(T(X)) {}
Default (T const & val) : val(val) {}
operator T & () { return val; }
operator T const & () const { return val; }
T val;
};
<...>
std::map<KeyType, Default<ValueType, DefaultValue> > mapping;
More General Version, Support C++98/03 and More Containers
Works with generic associative containers, the only template parameter is the container type itself.
Supported containers: std::map, std::multimap, std::unordered_map, std::unordered_multimap, wxHashMap, QMap, QMultiMap, QHash, QMultiHash, etc.
template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m,
const typename MAP::key_type& key,
const typename MAP::mapped_type& defval)
{
typename MAP::const_iterator it = m.find(key);
if (it == m.end())
return defval;
return it->second;
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Here is a similar implementation by using a wrapper class, which is more similar to the method get() of dict type in Python: https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp
template<typename MAP>
struct map_wrapper
{
typedef typename MAP::key_type K;
typedef typename MAP::mapped_type V;
typedef typename MAP::const_iterator CIT;
map_wrapper(const MAP& m) :m_map(m) {}
const V& get(const K& key, const V& default_val) const
{
CIT it = m_map.find(key);
if (it == m_map.end())
return default_val;
return it->second;
}
private:
const MAP& m_map;
};
template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
return map_wrapper<MAP>(m);
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
One workaround is to use map::at() instead of [].
If a key does not exist, at throws an exception.
Even nicer, this also works for vectors, and is thus suited for generic programming where you may swap the map with a vector.
Using a custom value for unregistered key may be dangerous since that custom value (like -1) may be processed further down in the code. With exceptions, it's easier to spot bugs.
Expanding on the answer https://stackoverflow.com/a/2333816/272642, this template function uses std::map's key_type and mapped_type typedefs to deduce the type of key and def.
This doesn't work with containers without these typedefs.
template <typename C>
typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) {
typename C::const_iterator it = m.find(key);
if (it == m.end())
return def;
return it->second;
}
This allows you to use
std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);
without needing to cast the arguments like std::string("a"), (int*) NULL.
Pre-C++17, use std::map::insert(), for newer versions use try_emplace(). It may be counter-intuitive, but these functions effectively have the behaviour of operator[] with custom default values.
Realizing that I'm quite late to this party, but if you're interested in the behaviour of operator[] with custom defaults (that is: find the element with the given key, if it isn't present insert a chosen default value and return a reference to either the newly inserted value or the existing value), there is already a function available to you pre C++17: std::map::insert(). insert will not actually insert if the key already exists, but instead return an iterator to the existing value.
Say, you wanted a map of string-to-int and insert a default value of 42 if the key wasn't present yet:
std::map<std::string, int> answers;
int count_answers( const std::string &question)
{
auto &value = answers.insert( {question, 42}).first->second;
return value++;
}
int main() {
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
return 0;
}
which should output 42, 43 and 44.
If the cost of constructing the map value is high (if either copying/moving the key or the value type is expensive), this comes at a significant performance penalty, which would be circumvented with C++17's try_emplace().
If you have access to C++17, my solution is as follows:
std::map<std::string, std::optional<int>> myNullables;
std::cout << myNullables["empty-key"].value_or(-1) << std::endl;
This allows you to specify a 'default value' at each use of the map. This may not necessarily be what you want or need, but I'll post it here for the sake of completeness. This solution lends itself well to a functional paradigm, as maps (and dictionaries) are often used with such a style anyway:
Map<String, int> myNullables;
print(myNullables["empty-key"] ?? -1);
Maybe you can give a custom allocator who allocate with a default value you want.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
With C++20 it is simple to write such getter:
constexpr auto &getOrDefault(const auto &map, const auto &key, const auto &defaultValue)
{
const auto itr = map.find(key);
return itr == map.cend() ? defaultValue : itr->second;
}
Here is a correct approach that will conditionally return a reference if the caller passes in an lvalue reference to the mapped type.
template <typename Map, typename DefVal>
using get_default_return_t = std::conditional_t<std::is_same_v<std::decay_t<DefVal>,
typename Map::mapped_type> && std::is_lvalue_reference_v<DefVal>,
const typename Map::mapped_type&, typename Map::mapped_type>;
template <typename Map, typename Key, typename DefVal>
get_default_return_t<Map, DefVal> get_default(const Map& map, const Key& key, DefVal&& defval)
{
auto i = map.find(key);
return i != map.end() ? i->second : defval;
}
int main()
{
std::map<std::string, std::string> map;
const char cstr[] = "world";
std::string str = "world";
auto& ref = get_default(map, "hello", str);
auto& ref2 = get_default(map, "hello", std::string{"world"}); // fails to compile
auto& ref3 = get_default(map, "hello", cstr); // fails to compile
return 0;
}
If you would like to keep using operator[] just like when you don't have to specify a default value other than what comes out from T() (where T is the value type), you can inherit T and specify a different default value in the constructor:
#include <iostream>
#include <map>
#include <string>
int main() {
class string_with_my_default : public std::string {
public:
string_with_my_default() : std::string("my default") {}
};
std::map<std::string, string_with_my_default> m;
std::cout << m["first-key"] << std::endl;
}
However, if T is a primitive type, try this:
#include <iostream>
#include <map>
#include <string>
template <int default_val>
class int_with_my_default {
private:
int val = default_val;
public:
operator int &() { return val; }
int* operator &() { return &val; }
};
int main() {
std::map<std::string, int_with_my_default<1> > m;
std::cout << m["first-key"] << std::endl;
++ m["second-key"];
std::cout << m["second-key"] << std::endl;
}
See also C++ Class wrapper around fundamental types