Creating an iterator to a map in a class template - c++

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));

Related

using a map with a comparator as a std::map parameter

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));
}

How can i get template's type if my instance is stl map?

I want a get_value template function.
please see the following code:
template<typename T>
(something i want) get_value(const T& m, int key) {
auto it = m.upper_bound(key);
return it == m.begin() ? (*it).second : (*--it).second; // please notice here,
// if my instance is map<int, map<string, int>> the return type should be m.second's type
// that's map<string, int>
}
int main() {
std::map<int, std::map<std::string, int>> m;
auto& it = get_value(m, 10);
}
as you can see, the template function should have a return type, which is depend on instance's second type, is there any method i can get this type to make the code runnable?
The "second type" in a std::map<K,V> is called std::map<K,V>::mapped_type. However, you can use auto to let the compiler deduce that for you:
template<typename T>
auto get_value(const T& m, int key) {
auto it = m.upper_bound(key);
return it == m.begin() ? (*it).second : (*--it).second; // please notice here,
}
or with explicit type:
template<typename T>
typename T::mapped_type get_value(const T& m, int key) {
auto it = m.upper_bound(key);
return it == m.begin() ? (*it).second : (*--it).second; // please notice here,
}
If you can use C++14 standard or above, the safest way to go would be to use decltype(auto) as the return type:
template<typename T>
decltype(auto) get_value(const T& m, int key);
The difference to a plain auto is that decltype(auto) preserves cv-qualifiers, and in your case you most likely want to forward exactly the same type that std::map gives you.
For example, since the actual code uses std::map<int, std::map<std::string, int>>, you might want to avoid copying the return value every time, and decltype(auto) will achieve that.

Can't Pass Enum Value to Recursive Template (C++)

Suppose I want to write a recursive template function that compares an individual value to every single element in an n-dimensional vector, returning true if there is at least one match and false if there are none.
I wrote some code to do this, although it's probably far from optimal:
template <typename T, typename Checker>
void check_for_each(const T& v, const Checker condition)
{
condition(v);
}
template <typename T, typename Checker>
void check_for_each(const std::vector<T>& v, const Checker condition)
{
for(unsigned int i = 0; i < v.size(); i++)
{
check_for_each(v[i], condition);
}
}
template <typename T, typename U>
bool is_equal_any(const T& VALUE, const std::vector<typename U> VECTOR)
{
bool is_equal = false;
check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
{
if(!is_equal && VALUE == val)
{
is_equal = true;
}
});
return is_equal;
}
While this seems to work, I've encountered an unusual issue and I can't quite understand it. For example, the following code works:
enum PIECE_NAME {EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING};
std::vector<std::vector<int>> board {{ROOK, BISHOP}, {KNIGHT, QUEEN}};
std::cout << is_equal_any(2, board); // outputs 1 (there is a rook on the board)
Yet the following, slight change does not:
std::cout << is_equal_any(ROOK, board); // compile error C2664
Apparently my function cannot figure out how to convert the enum value to an integer. Of course, I can just use static_cast<int>(ROOK), and the code compiles and runs as expected, but that's obviously not ideal. Furthermore, I know that I can change my board's declaration to std::vector<std::vector<PIECE_NAME>> board, (which also runs as expected) but I'd prefer to leave it at int. So is it possible to rewrite these recursive template functions so that is_equal_any can take enum values directly? I'm still very new to C++, so I would really appreciate as much detail in your answer as possible. Thank you.
The problem arises from the type T here:
check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
^
By calling
is_equal_any(ROOK, board)
T is a PIECE_NAME, but what you are finally passing as parameter to this lambda are the elements of your vector of type int. But an int can't be implicitly converted to an enum.
You can't either use directly U as it could be a std::vector<int> or std::vector< std::vector<int> > or...
If you were using C++14, you could use a generic lambda with auto:
check_for_each(VECTOR, [&is_equal, &VALUE](const auto& val)
But as you tagged your question C++11, you can use a trait:
template <typename T>
struct leaf_type {
using type = T;
};
template <typename T>
struct leaf_type<std::vector<T>> {
using type = typename leaf_type<T>::type;
};
template <typename T>
using leaf_type_t = typename leaf_type<T>::type;
usage:
check_for_each(VECTOR, [&is_equal, &VALUE](const leaf_type_t<U> & val)
DEMO
Btw you should avoid nested std::vectors and linearize it into a single one like:
std::vector<int> board {ROOK, BISHOP, KNIGHT, QUEEN};
Then you can easily use std::find.
This is kind of an XY problem, as there are better approaches:
Use a scoped enum
don't mix integers and enums
delegate your work to std::any_of
For example:
namespace multi_dim{
template< class InputIt, class UnaryPredicate >
bool any_of(InputIt first, InputIt last, UnaryPredicate p)
{
using std::any_of;
for(;first != last; ++first)
{
bool next = any_of(first->cbegin(), first->cend(), p);
if (next)
return true;
}
return false;
}
}
Demo
A test:
std::vector<std::vector<PIECE>> board {{PIECE::ROOK, PIECE::BISHOP}, {PIECE::KNIGHT, PIECE::QUEEN}};
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::ROOK;}) << std::endl;
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::EMPTY;}) << std::endl;
Output:
true
false
Wild guess: Try a difference template parameter for VALUE and for the element type of VECTOR.
I don't use MSVC so I'm not sure exactly what error you're getting.
... regardless of all that, I just have to repeat my comment: Please don't write this kind of code.
Although the solution using std::any_of is the best one, I give my answer that show somewhat less drastic improvement to original code.
template <typename T, typename Checker>
bool check_for_each(const T& v, const Checker condition)
{
return condition(v);
}
template <typename T, typename Checker>
bool check_for_each(const std::vector<T>& v, const Checker condition)
{
return std::find_if(begin(v), end(v), [condition](const T &t) { return check_for_each(t, condition); }) != v.end();
}
template <typename T, typename U>
bool is_equal_any(const T& value, const U &container)
{
return check_for_each(container, [&value](const T& val){ return value == val; });
}
enum class PIECE_NAME { EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING };
void test()
{
std::vector<std::vector<PIECE_NAME>> board
{
{ PIECE_NAME::ROOK, PIECE_NAME::BISHOP },
{ PIECE_NAME::KNIGHT, PIECE_NAME::QUEEN }
};
std::cout << is_equal_any(PIECE_NAME::ROOK, board);
}
This solution is still hard-coded for nested vectors but the code has been simplified a bit and also optimized as it will stop searching once an item is found.
As already suggested in other comments, you should really use enum (or better yet enum class). It does not make much sense to store integers in the vector as you loose type information.

Copy map/multimap keys to vector or set

I have written below code to convert keys in map or multimap to set:
template<typename STLContainer>
inline auto CopyContanerKeyToSet(const STLContainer& cont)
{
std::set<decltype(cont.begin()->first)> lset;
std::transform(cont.begin(),cont.end(),std::inserter(lset,lset.end()),[](const auto it) { return it.first;});
return lset
}
Now there is requirement that sometimes I need to convert keys into vector as well. So I just want to know how to write template function that can accept vector or set as template argument and after that create that container accordingly.
We can solve this with a Template template parameter. This allows us to specify just the main type without specifying the template type of that type. Doing that gives us
template< template<typename ...> class OutputContainer, typename STLContainer>
inline auto CopyContanerKeyToSet(const STLContainer& cont)
{
OutputContainer<typename STLContainer::key_type> lset;
std::transform(cont.begin(),cont.end(),std::inserter(lset,lset.end()),[](const auto it) { return it.first;});
return lset;
}
And then we can use that with something like this
int main()
{
std::map<std::string, int> foo{ {"this", 1}, {"second", 1} };
auto output = CopyContanerKeyToSet<std::vector>(foo);
for (const auto& e : output)
std::cout << e << " ";
}
Which gives us
second this
Live Example
I also changed <decltype(cont.begin()->first)> to <typename STLContainer::key_type> as value_type of the map/multimap has a const key_type for the std::pair which we do not want for the vector/set.

std::map default value

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