Inserting in map<string, STRUCT> error - c++

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.

Related

How to use std::pair with classes?

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

Creating a Variant class and std::map<Variant, Variant>

I craeted a simple Variant class to store string, integer, double, etc. I'm trying to use a map of type std::map<Variant, Variant> but I'm getting this strange error:
In file included from /usr/include/c++/7/string:48:0,
from /home/dev/proj/cpp/common/Variant.h:3,
from /home/dev/proj/cpp/common/Event.h:3,
from /home/dev/proj/cpp/common/Event.cpp:1:
/usr/include/c++/7/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Variant]':
/usr/include/c++/7/bits/stl_map.h:511: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 = Variant; _Tp = Variant; _Compare = std::less<Variant>; _Alloc = std::allocator<std::pair<const Variant, Variant> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = Variant; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = Variant]'
/home/dev/orwell/cpp/common/Event.cpp:33:18: required from here
/usr/include/c++/7/bits/stl_function.h:386:20: error: no match for 'operator<' (operand types are 'const Variant' and 'const Variant')
{ return __x < __y; }
~~~~^~~~~
This is my Variant class:
class Variant
{
public:
enum class Type
{
Integer,
Double,
String
};
Variant()
{
}
Variant(int integer)
{
this->type = Type::Integer;
setInteger(integer);
}
Variant(std::string string)
{
this->type = Type::String;
setString(string);
}
Variant(double _double)
{
this->type = Type::Double;
setDouble(_double);
}
Type type;
This is where the error is happening:
void Event::add(std::string key, std::string value) {
this->map[key] = Variant(value); //problem here
}
std::map is a sorted array. To do that it uses the < operator.
Thus, if you want to use Variant in a map (I believe this only applies to keys), you will need to supply an operator<() for it. You can find some examples here.
Or, you'll need a comparison function. That can work too.

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.

initialize a set with a comparison structure that depends on "this" c++

I want to define a set that does its comparison based on the value of other members of the current class:
std::set<ContentType> mySet(doComparison(*this));
where doCompare is a struct:
struct doCompare{
doCompare( MyClass& mc ) : _mc(mc) { }
MyClass& _mc;
bool operator()( const ContentType & i1, const ContentType & i2 ){
return _mc.otherMember[i1] < _mc.otherMemeber[i2];
}
};
Here the mySet is a member of MyClass and when I try to initialize the set with the comparison function in the initialization list: mySet(doCompare(*this)) the code does not compile.
What am I doing wrong here?
The error is:
no matching function for call to ``std::set<ContentType>::set(MyClass::doCompare)`
Here is the full message (with the change of names for better readability):
./myclass.h:74:165: error: no matching function for call to ‘std::set<ContentType>::set(MyClass::doCompare)’
: mySet(doCompare(*this)) {
^
./myclass.h:74:165: note: candidates are:
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
from [blah blah]
/usr/include/c++/4.8/bits/stl_set.h:193:7: note: std::set<_Key, _Compare, _Alloc>::set(const std::set<_Key, _Compare, _Alloc>&) [with _Key = ContentType; _Compare = std::less<ContentType >; _Alloc = std::allocator<ContentType >]
set(const set& __x)
^
/usr/include/c++/4.8/bits/stl_set.h:193:7: note: no known conversion for argument 1 from ‘MyClass::doCompare’ to ‘const std::set<ContentType >&’
/usr/include/c++/4.8/bits/stl_set.h:180:2: note: template<class _InputIterator> std::set<_Key, _Compare, _Alloc>::set(_InputIterator, _InputIterator, const _Compare&, const allocator_type&)
set(_InputIterator __first, _InputIterator __last,
^
/usr/include/c++/4.8/bits/stl_set.h:180:2: note: template argument deduction/substitution failed:
In file included from [blahblah]:
./myclass.h:74:165: note: candidate expects 4 arguments, 1 provided
: mySet(doCompare(*this)) {
^
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
/usr/include/c++/4.8/bits/stl_set.h:163:2: note: template<class _InputIterator> std::set<_Key, _Compare, _Alloc>::set(_InputIterator, _InputIterator)
set(_InputIterator __first, _InputIterator __last)
^
/usr/include/c++/4.8/bits/stl_set.h:163:2: note: template argument deduction/substitution failed:
In file included from [blahblah]:
./myclass.h:74:165: note: candidate expects 2 arguments, 1 provided
: mySet(doCompare(*this)) {
^
In file included from /usr/include/c++/4.8/set:61:0,
from ./myclass.h:12,
from blahblah:
/usr/include/c++/4.8/bits/stl_set.h:148:7: note: std::set<_Key, _Compare, _Alloc>::set(const _Compare&, const allocator_type&) [with _Key = ContentType; _Compare = std::less<ContentType >; _Alloc = std::allocator<ContentType >; std::set<_Key, _Compare, _Alloc>::allocator_type = std::allocator<ContentType >]
set(const _Compare& __comp,
^
/usr/include/c++/4.8/bits/stl_set.h:148:7: note: no known conversion for argument 1 from ‘MyClass::doCompare’ to ‘const std::less<ContentType >&’
/usr/include/c++/4.8/bits/stl_set.h:139:7: note: std::set<_Key, _Compare, _Alloc>::set() [with _Key = ContentType; _Compare = std::less<ContentType>; _Alloc = std::allocator<ContentType >]
set()
^
To summarize the issue:
It seems I can't initialize the set with the comparison in its declaration, because it depends on this.
The initialization with comparison function after the set is declared is failing, but I don't know why.
SOLUTION thanks to #WhozCraig
declare mySet as:
std::set<ContentType, doCompare> mySet;
and initialize it in the initialization list as:
mySet(doCompare(*this))
You're not declaring your set type with the proper template comparator parameter. This:
std::set<ContentType> mySet;
means this when expanded:
std::set<ContentType, std::less<ContentType>> mySet
leaving out the allocator for brevity. This means when constructing mySet and specifying an alternate comparator functor, it must be of type std::less<ContentType>, but yours is not. It is of type doCompare. The compiler tries to match against every other constructor argument list, failing to find any match, finally resulting in your error.
Change your declaration of mySet to:
std::set<ContentType,doCompare> mySet;
Now the types should wire up correctly.
And as I said in comments, i see no reason the reference to your MyClass being held in your comparator object should be non-const. unless you can think of a good reason, I suggest changing the reference to const instead, i.e. const MyClass&

Trouble with std::map::emplace syntax

I am trying to emplace data into a std::map. Below is what I have tried (trimmed from the original source but definitely gives the idea):
template<typename T> class trie {
private:
std::map<typename T::value_type, std::unique_ptr<trie<T>>> children;
std::unique_ptr<trie<T>> parent;
// Later
public:
trie(const trie<T>& other, trie<T>* const parent) :
parent{parent}
{
for(auto const &it : other.children)
children.emplace(it.first, {*it.second});
}
};
The error is as follows:
trie.h: In instantiation of ‘trie<T>::trie(const trie<T>&, trie<T>*) [with T = std::basic_string<char>]’:
main.cpp:7:23: required from here
trie.h:90:3: error: no matching function for call to ‘std::map<char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > >, std::less<char>, std::allocator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > > >::emplace(const char&, <brace-enclosed initializer list>)’
children.emplace(it.first, {*it.second});
^
trie.h:90:3: note: candidate is:
In file included from /usr/include/c++/4.8.1/map:61:0,
from trie.h:4,
from main.cpp:2:
/usr/include/c++/4.8.1/bits/stl_map.h:540:2: note: std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {}; _Key = char; _Tp = std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > >; _Compare = std::less<char>; _Alloc = std::allocator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > >]
emplace(_Args&&... __args)
^
/usr/include/c++/4.8.1/bits/stl_map.h:540:2: note: candidate expects 0 arguments, 2 provided
So my question is:
How do I correctly initialize the map element, the goal being a deep copy of the pointed-to trie, and no needless copies/moves?
Thanks in advance!
By passing {*it.second} as the initialiser for the value, you're effectively trying to initialise a std::unique_ptr<trie<T>> with a trie<T>. I believe you're looking for this:
public:
trie(const trie<T>& other, trie<T>* const parent) :
parent{parent}
{
for(auto const &it : other.children) {
// Separate creation of unique_ptr for exception safety, thanks to #DanielFrey
std::unique_ptr<trie<T>> p(new trie<T>(*it.second));
children.emplace(it.first, std::move(p));
}
}
Note that you will also have to provide a copy constructor, because the default one is deleted, as your class has non-copyable members.
Unrelated to the question, but you should reconsider your design: you most likely have an ownership loop. If a trie<T> stores a unique_ptr to its children and these store a unique_ptr back to their parent, you'll get double deletion errors. Turn one of these (probably the pointer to parent) into a raw pointer. Raw pointers are fine for observing without participating in ownership.
You need
for(auto const &it : other.children) {
std::unique_ptr<trie<T>> element(new trie<T>(*it.second));
children.emplace(it.first, std::move(element));
}
to prevent a resource leak in case an exception is thrown from emplace. If available (C++14), you could simplify the code to
for(auto const &it : other.children) {
children.emplace(it.first, std::make_unique<trie<T>>(*it.second));
}
As a rule of thumb for all smart pointers, you always use std::make_* or you must use a separate line to create each of them.