How to use std::pair with classes? - c++

so I'm experimenting with cvc5 and just wanted to keep track of the Terms in a map so I have created this:
std::map<std::string, std::pair<Term, int>> terms;
Basically, for I used the name as an index and I store the Term with other info in the map.
I have created a subtype of Term called TermStruct and I wanted to create another similar map:
std::map<std::string, std::pair<TermStruct, int>> termsStructs;
TermStruct was created roughly in the following way
class TermStruct : public Term {
public:
TermStruct(Term *t) : Term() {
this->t = t;
}
bool isNull();
Term *getTerm() { return this->t; };
std::string toString();
private:
Term *t = nullptr;
};
Now when I tried to add a new element to the termStructs map in the following way:
termsStructs[str] = std::pair(term, offset);
Note: term is of the correct type.
I have a number of compilation error such as:
/usr/include/c++/11/bits/stl_map.h:501:37: required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::__cxx11::basic_string<char>; _Tp = std::pair<TermStruct, int>; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, std::pair<TermStruct, int> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::pair<TermStruct, int>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::__cxx11::basic_string<char>]’
/home/alberto/progetti/llvm/plugin/runtime/cvc5/Runtime.cpp:113:25: required from here
/usr/include/c++/11/tuple:1824:9: error: no matching function for call to ‘std::pair<TermStruct, int>::pair()’
1824 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
Any idea why and how to fix it?
Thanks

pair has nothing to do with this problem. It is all about map. I see two options.
Introduce a default constructor(a constructor without parameters) for TermStruct if you want to use std::map::operator[]. Here's why:
termsStructs[str] = std::pair(term, offset) does not insert the pair object right away
termsStructs[str] first creates a new key-value pair and adds it to the map(if there is no entry for str)
It is done by running std::make_pair(key, T())
As T here is std::pair, it tries to call Term() (FYI the second(integer) is zero-initialized)
However there is no Term() defined - compiler error
You may refer these links for details.
https://en.cppreference.com/w/cpp/container/map/operator_at (1)
https://en.cppreference.com/w/cpp/utility/pair/pair (1)
Or you can use emplace or insert.
These methods look uglier than the above but they behave just like what you must have expected. It does not require a default constructor.
For example,
termsStructs.emplace(str, std::pair(term, offset));
termsStructs.insert({str, std::pair(term, offset)});

Related

How to define a map from function pointers to strings

I am trying to define a map from member function pointers to strings in C++. But the the compiler complains that operator < is not defined for member function pointers. Is there a way to do this?
Example code:
#include <map>
#include <string>
class foo;
typedef void (foo::*fooFunc)();
class foo {
public:
foo() {
myMap[func]="example function";
}
void func() {};
std::map<fooFunc,std::string> myMap;
};
Resulting error message:
In file included from C:/msys64/mingw64/include/c++/6.2.0/bits/stl_tree.h:65:0,
from C:/msys64/mingw64/include/c++/6.2.0/map:60,
from grr.cpp:1:
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = void (foo::*)()]':
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_map.h:501:32: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = void (foo::*)(); _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<void (foo::*)()>; _Alloc = std::allocator<std::pair<void (foo::* const)(), std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = void (foo::*)()]'
grr.cpp:9:15: required from here
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h:386:20: error: invalid operands of types 'void (foo::* const)()' and 'void (foo::* const)()' to binary 'operator<'
{ return __x < __y; }
~~~~^~~~~
std::map requires a comparison operator for its keys. By default, it uses operator <. Since operator < is not defined for pointer-to-member types, you are getting this error.
To fix it you'll need to define a custom comparison operator and provide it as a parameter to std::map. Alternatively, use a different container.
From cppreference:
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black
trees
If you want options on how to write such a comparison operator, it has already been answered here.

How to achieve const-correctness in multidimensional map in C++

I have a complex map that, in the end, stores pointers to Drawable objects. Drawable objects have a draw() member function which is declared as const. I need to call all the draw functions for all objects stored in my map that are of a certain type, and I must do it inside a const function. However I can't seem to be able to preserve the const-correctness of my function (drawSolid).
My outer map (map<int, ##>) is essentially indexing some sub-maps. Sub-maps are, in turn, indexing vectors (map<ItemType, vector<##> >). Finally, this vector keeps a set of shared_ptr<Drawable> objects.
If I remove the const qualifier from my function header, everything compiles, but I need it to be const. How may I iterate through my multidimensional map, preserving const-correctness?
void DrawableItems::drawSolid(int item_list = -1) const
{
typedef std::map<int, std::map<ItemType, std::vector<std::shared_ptr<Drawable> > > > drawablemap;
std::vector<std::shared_ptr<Drawable> > its;
for(drawablemap::const_iterator li = __items.begin(); li != __items.end(); li++) {
its = li->second[SOLID];
for(auto di = its.begin(); di != its.end(); di++) {
di->get()->draw();
}
}
}
This the error I get from the compiler (G++):
/.../dss-sim/src/graphics/DrawableItems.cpp: In member function ‘void DrawableItems::drawSolid(int) const’:
/.../dss-sim/src/graphics/DrawableItems.cpp:51:35: error: passing ‘const std::map<DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > >’ as ‘this’ argument discards qualifiers [-fpermissive]
its = li->second[SOLID];
^
In file included from /usr/include/c++/5/map:61:0,
from /.../dss-sim/src/common/dss.hpp:11,
from /.../dss-sim/src/graphics/DrawableItems.hpp:19,
from /.../dss-sim/src/graphics/DrawableItems.cpp:15:
/usr/include/c++/5/bits/stl_map.h:494:7: note: in call to ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = DrawableItems::ItemType; _Tp = std::vector<std::shared_ptr<Drawable> >; _Compare = std::less<DrawableItems::ItemType>; _Alloc = std::allocator<std::pair<const DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::vector<std::shared_ptr<Drawable> >; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = DrawableItems::ItemType]’
operator[](key_type&& __k)
There is no const version of operator[] in std::map. However, there is a const version of at() which you can use instead:
its = li->second.at(SOLID);
The reason is that operator[] inserts an element if there is no element yet, so there can not be a const version of operator[].
at() on the other hand throws an exception if no element exists, and this is compatible with a const std::map.

Inserting in map<string, STRUCT> error

I have a map with the struct defined as under:
struct kv_string {
std::string value;
long long exp_time;
kv_string(const std::string& v): value(v), exp_time(-1) {}
};
Now when I'm trying to insert a new structure using
else if(qargs[0] == "set"){
if(qargs.size()==3){
kv_map.insert(std::make_pair( qargs[1], kv_string(qargs[2])));
}
}
(qargs is a vector<string>), I get the following error:
> In file included from /usr/include/c++/4.8/map:61:0,
> from structures.h:5:
> /usr/include/c++/4.8/bits/stl_map.h: In instantiation of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key,
> _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = kv_string; _Compare =
> std::less<std::basic_string<char> >; _Alloc =
> std::allocator<std::pair<const std::basic_string<char>, kv_string> >;
> std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = kv_string;
> std::map<_Key, _Tp, _Compare, _Alloc>::key_type =
> std::basic_string<char>]’:
> /usr/include/c++/4.8/stdexcept:281:48: required from here
> /usr/include/c++/4.8/bits/stl_map.h:469:59: error: no matching function for call to ‘kv_string::kv_string()’
> __i = insert(__i, value_type(__k, mapped_type()));
> ^
> /usr/include/c++/4.8/bits/stl_map.h:469:59: note: candidates are:
> structures.h:11:9: note: kv_string::kv_string(const string&)
> kv_string(const std::string& v): value(v), exp_time(-1) {}
> ^
> structures.h:11:9: note: candidate expects 1 argument, 0 provided
> structures.h:8:8: note: kv_string::kv_string(const kv_string&)
> struct kv_string {
> ^
> structures.h:8:8: note: candidate expects 1 argument, 0 provided
> make: *** [server_main.o] Error 1
I have also tried adding an additional constructor kv_string(){}, but it gives a segmentation fault.
You want this:
kv_map.insert(std::make_pair(qargs[1], kv_string(qargs[2]));
Or this:
kv_map.emplace(qargs[1], kv_string(qargs[2]);
Or, in C++17:
kv_map.try_emplace(qargs[1], qargs[2]);
The []-operator default-initializes a new element (if one doesn't exist for the given key), but your type kv_string is not default-constructible. So you cannot use that operator. The above operations are more powerful than the []-operator, too: they return an iterator to the element at the key, and information about whether the key already existed.
The C++ compiler emitted this error message complaining about the lack of a default constructor for your kv_string class:
/usr/include/c++/4.8/bits/stl_map.h:469:59: error: no matching function for call to ‘kv_string::kv_string()’
__i = insert(__i, value_type(__k, mapped_type()));
If you read the documentation for std::map::operator[], you'll see that the mapped type (in your case kv_string) must be default-constructible.
So, if it makes sense for your own design, you could just add a default constructor to your kv_string struct:
struct kv_string {
// ...
// Default constructor
kv_string() : exp_time(-1 /* or whatever default value */) {}
};
As a side note, I would also mark your kv_string(const std::string&) constructor as explicit, to avoid implicit conversions from strings.

Accessing map elements [duplicate]

This question already has answers here:
Why isn't the [] operator const for STL maps?
(6 answers)
Closed 8 years ago.
I try to return a specific object of my std::map as follows :
const Vertex& Graph::getVertex(const std::pair<size_t, size_t>& pos) const // -> compile error
{
return this->_vertices[std::get<0>(pos)][std::get<1>(pos)];
}
Map :
std::map<size_t, std::vector<Vertex>> _vertices;
However, I get a compilation error if I let the constness of my getVertex function. Does it mean that accessing my map elements this way actually modify its instance ? Is there a better way to access my map elements ?
The error :
error: passing ‘const std::map<unsigned int, std::vector<Vertex> >’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::o\
perator[](const key_type&) [with _Key = unsigned int; _Tp = std::vector<Vertex>; _Compare = std::less<unsigned int>; _Alloc = std::allocator<std::pair<const unsigned int, std::vector<Vertex> > >; std::map\
<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::vector<Vertex>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = unsigned int]’ discards qualifiers [-fpermissive]
getVertex is const member function
this->_vertices[std::get<0>(pos)][std::get<1>(pos)]; can modify the _vertices map using std::map::operator[] when key doesn't exists , hence the error

Find in a map using the base class with a boost::shared_ptr

I'm looking for a way to find an element inside a map using the base class (the code bellow is just a basic example):
#include <map>
#include <boost/shared_ptr.hpp>
class Base {
public:
Base(int v) : id(v) {};
int id;
};
class Derived : public Base {
public:
Derived(int v) : Base(v) {};
};
int main()
{
std::map<boost::shared_ptr<Derived>, double> m;
m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(1)), 10));
m.insert(std::make_pair(boost::shared_ptr<Derived>(new Derived(2)), 20));
auto b1 = boost::shared_ptr<Base>(new Base(1));
m.find(b1);
return 0;
}
Basically, I want to compare the id attribute. The errors returned by the compiler are the following:
main.cpp: In function 'int main()':
main.cpp:35:14: error: no matching function for call to 'std::map<boost::shared_ptr<Derived>, double>::find(boost::shared_ptr<Base>&)'
m.find(b1);
^
main.cpp:35:14: note: candidates are:
In file included from /usr/include/c++/4.8/map:61:0,
from main.cpp:1:
/usr/include/c++/4.8/bits/stl_map.h:820:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
find(const key_type& __x)
^
/usr/include/c++/4.8/bits/stl_map.h:820:7: note: no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& {aka const boost::shared_ptr<Derived>&}'
/usr/include/c++/4.8/bits/stl_map.h:835:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const key_type&) const [with _Key = boost::shared_ptr<Derived> _Tp = double; _Compare = std::less<boost::shared_ptr<Derived> > _Alloc = std::allocator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::pair<const boost::shared_ptr<Derived>, double> > std::map<_Key, _Tp, _Compare, _Alloc>::key_type = boost::shared_ptr<Derived>]
find(const key_type& __x) const
^
/usr/include/c++/4.8/bits/stl_map.h:835:7: note: no known conversion for argument 1 from 'boost::shared_ptr<Base>' to 'const key_type& {aka const boost::shared_ptr<Derived>&}'
If you want to use your map for lookup by id, you need to pass in an appropriate comparison function so that the map sorts its keys by id instead of the default operator < (which, I believe, compares ownership block addresses with boost::shared_ptr arguments).
So change the map like this:
struct Less_id
{
bool operator() (const boost::shared_ptr<Derived> &lhs, const boost::shared_ptr<Derived> &rhs) const
{
return lhs->id < rhs->id;
}
};
typedef std::map<boost::shared_ptr<Derived>, double, Less_id> Map;
Map m;
This will sort the map accordingly, but still not allow lookup by Base pointer. To do that, you can write your own function above std::lower_bound:
Map::const_iterator find_base(const Map &map, const boost::shared_ptr<Base> &base)
{
auto it = std::lower_bound(
map.begin(), map.end(), base,
[](const Map::value_type &lhs, const boost::shared_ptr<Base> &rhs)
{ return lhs.first->id < rhs->id; }
);
if (it != map.end() && it->first->id == base->id)
return it;
else
return map.end();
}
std::lower_bound() is used to keep the logarithmic complexity std::map::find() offers.
Live example
Use an
std::map<boost::shared_ptr<Derived>, double, std::less<boost::shared_ptr<Base>>>
Edit: I am ahead of the times - this only works for C++14.
There are two issues to overcome. The first is that you want to search for a Base in a collection of Deriveds. The second is that you want to compare by value rather than by address. The other answers are neglecting this second point. Try std::find_if:
auto b1 = boost::shared_ptr<Base>(new Base(1));
auto itFound = std::find_if(m.begin(), m.end()
[=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
{
// omitting null checks for this example
return pair.first->id == b1->id;
});
And if the requirement is really just to find a key with the given id, you could make it simpler:
int queryKey = 1;
auto itFound = std::find_if(m.begin(), m.end()
[=](const std::pair<boost::shared_ptr<Derived>, double>& pair)
{
return pair.first->id == queryKey;
});
Now, as noted in the comments, this will give you linear rather than map's usual logarithmic lookup time. If the map is small it won't matter, but if this is an issue, you could use std::lower_bound instead of find_if. Note that this would also require adding a custom comparer so you could ensure the map's sort order was based on id. For example:
struct Compare
{
bool operator()(const boost::shared_ptr<Derived>& l,
const boost::shared_ptr<Derived>& r) const
{
// omitting null checks for this example
return l->id < r->id;
}
};
std::map<boost::shared_ptr<Derived>, double, Compare> m;
This is because boost::shared_ptr's operator < is not what you want, you need a delegation to the Derived class's operator <.
use std::map<_Key, _Tp, _Compare>
for example:
std::map<boost::shared_ptr<Derived>, double, compare_func()> m;
As you are looking for an object Base which can only be in the map if it is of type Derived you can simply do this:
boost::shared_ptr<Derived> d1 = boost::dynamic_pointer_cast<Derived>(b1);
if(d1) {
m.find(d1);
} else {
// not in the map
}
As you use std::shared_ptr<Derived> as a key, another possibility would be to actually use pointers to the base class instead:
Use
std::map<boost::shared_ptr<Base>, double> m;
instead of
std::map<boost::shared_ptr<Derived>, double> m;
and everything works as expected.
Btw, you are missing a virtual destructor in Base!