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.
Related
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.
typedef struct {
string strDatabaseName;
set <string, greater<string> > setDBAccName;
} UserDBAInfo_t;
typedef struct {
map<int, UserDBAInfo_t > mapUserDBAInfo;
} UserDBInfo_t;
typedef set<string, greater<string> > setNames_t;
int main( int argc, char * argv[] )
{
...
map<string, UserDBInfo_t > mapHRUserDBInfo;
UserDBInfo_t structUserDBInfo;
UserDBAInfo_t structUserDBAInfo;
structUserDBAInfo.strDatabaseName = strDatabaseName;
structUserDBAInfo.setDBAccName.insert(strDBAccName);
structUserDBInfo.mapUserDBAInfo.insert(nDatabaseID, structUserDBAInfo);
mapHRUserDBInfo.insert(make_pair(strSabun, structUserDBInfo)); <--- compile error here
...
}
When I compile it, I got error message.
main.cpp:2778: error: no matching function for call to 'std::map<int, UserDBAInfo_t, std::less<int>, std::allocator<std::pair<const int, UserDBAInfo_t> > >::insert(int&, UserDBAInfo_t&)'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:395: note: candidates are: 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>::insert(const std::pair<const _Key, _Tp>&) [with _Key = int, _Tp = UserDBAInfo_t, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, UserDBAInfo_t> >]
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:419: note: 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::map<_Key, _Tp, _Compare, _Alloc>::insert(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, const std::pair<const _Key, _Tp>&) [with _Key = int, _Tp = UserDBAInfo_t, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, UserDBAInfo_t> >]
What might be wrong?
The error message, no matching function for call to 'std::map, std::allocator > >::insert(int&, UserDBAInfo_t&), indicates to me that the problem is in the line:
structUserDBInfo.mapUserDBAInfo.insert(nDatabaseID, structUserDBAInfo);
not the line you mentioned in your question. That should be:
structUserDBInfo.mapUserDBAInfo.insert(make_pair(nDatabaseID, structUserDBAInfo));
If you are able to use a C++11 compiler, you can also use:
structUserDBInfo.mapUserDBAInfo.emplace(nDatabaseID, structUserDBAInfo);
This question already has answers here:
C++ Converting function pointer to unique “hash” key
(2 answers)
Closed 9 years ago.
I am writing in C++, trying to compile under Ubuntu, and I am experiencing some issues with a map using function pointers as keys. When I define the map, I get no compiling errors, but as soon as I try to insert an element, I get a rather wordy
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from main.cpp:1:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = int (MyClass::*)()]’:
/usr/include/c++/4.6/bits/stl_tree.h:1277:4: instantiated from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = int (MyClass::*)(), _Val = std::pair<int (MyClass::* const)(), std::vector<int> >, _KeyOfValue = std::_Select1st<std::pair<int (MyClass::* const)(), std::vector<int> > >, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<int> > >]’
/usr/include/c++/4.6/bits/stl_map.h:518:41: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::map<_Key, _Tp, _Compare, _Alloc>::value_type>::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const value_type&) [with _Key = int (MyClass::*)(), _Tp = std::vector<int>, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<int> > >, typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::map<_Key, _Tp, _Compare, _Alloc>::value_type>::other>::iterator = std::_Rb_tree_iterator<std::pair<int (MyClass::* const)(), std::vector<int> > >, std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<int (MyClass::* const)(), std::vector<int> >]’
main.cpp:36:51: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:236:22: error: invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
Here is the example that caused the above error message:
#include <iostream>
#include <map>
#include <vector>
// class definition
class MyClass
{
public:
int f1(void);
int f2(void);
};
int MyClass::f1(void)
{
return 1;
}
int MyClass::f2(void)
{
return 2;
}
using namespace std;
int main( int argc, char* argv[] )
{
// define map
map< int (MyClass::*)(void), vector<int> > myMap;
vector<int> myVector;
//myMap[ &MyClass::f1 ] = myVector;
myMap.insert( make_pair( &MyClass::f1, myVector) );
return 0;
}
What could be the issue? I tried with both insert and [] assign, and I get the same error. Browsing the forums, I found this; but could that be the issue? I don't think I need to define an operator "<" for function pointers (shouldn't they behave as regular pointers?) ...or do I?
The error is telling you all you need to know:
invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
You cannot compare member function pointers using standard operator<, so you must provide a custom comparator when declaring your map.
Unfortunately, pointers to member functions cannot be compared for inequality, so you cannot define a comparison operator or use a std::mapin this case. I suggest using std::unordered_map, which only needs a std::hash and equality comparison, which you can do. See here for hashing and here for equality comparison.
You could implement the template specialization for less< int (MyClass::* const)() >, like follows:
typedef int (MyClass::*tMyClassMember)();
namespace std {
template<>
struct less<tMyClassMember>
{
bool operator()(const tMyClassMember& k1, const tMyClassMember& k2) const
{
auto p1 = reinterpret_cast<const intptr_t*>(&k1);
auto p2 = reinterpret_cast<const intptr_t*>(&k2);
return *p1 < *p2;
}
};
}
There may be better ways to compare pointer-to-members than "casting" them to integers, which is an implementation-specific hack, according to this question. That questions contains details about how to do that.
I'm trying to use custom allocator with C++ STL containers, and it works with vector, but fails with map. Some strange error regarding mmap_allocator<std::_Rb_tree_node<std::pair<const int, int> > > and not using mmap_allocator<std::pair<const int, int> > as I was expecting
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/map:60,
from 4.cpp:2:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h: In member function ‘_Alloc std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::get_allocator() const [with _Key = int, _Val = std::pair<const int, int>, _KeyOfValue = std::_Select1st<std::pair<const int, int> >, _Compare = std::less<int>, _Alloc = mmap_allocator<std::pair<const int, int> >]’:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h:383: instantiated from ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_destroy_node(std::_Rb_tree_node<_Val>*) [with _Key = int, _Val = std::pair<const int, int>, _KeyOfValue = std::_Select1st<std::pair<const int, int> >, _Compare = std::less<int>, _Alloc = mmap_allocator<std::pair<const int, int> >]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h:972: instantiated from ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_erase(std::_Rb_tree_node<_Val>*) [with _Key = int, _Val = std::pair<const int, int>, _KeyOfValue = std::_Select1st<std::pair<const int, int> >, _Compare = std::less<int>, _Alloc = mmap_allocator<std::pair<const int, int> >]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h:614: instantiated from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::~_Rb_tree() [with _Key = int, _Val = std::pair<const int, int>, _KeyOfValue = std::_Select1st<std::pair<const int, int> >, _Compare = std::less<int>, _Alloc = mmap_allocator<std::pair<const int, int> >]’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_map.h:87: instantiated from here
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_tree.h:354: error: no matching function for call to ‘mmap_allocator<std::pair<const int, int> >::mmap_allocator(const mmap_allocator<std::_Rb_tree_node<std::pair<const int, int> > >&)’
4.cpp:37: note: candidates are: mmap_allocator<T>::mmap_allocator(const mmap_allocator<T>&) [with T = std::pair<const int, int>]
4.cpp:36: note: mmap_allocator<T>::mmap_allocator() [with T = std::pair<const int, int>]
Here is the code:
#include <vector>
#include <map>
#include <stdio.h>
static size_t alloc;
template <typename T>
class mmap_allocator: public std::allocator<T>
{
public:
typedef size_t size_type;
typedef T* pointer;
typedef const T* const_pointer;
template<typename _Tp1>
struct rebind
{
typedef mmap_allocator<_Tp1> other;
};
pointer allocate(size_type n, const void *hint=0)
{
fprintf(stderr, "Alloc %d bytes.\n", n);
alloc += n;
return std::allocator<T>::allocate(n, hint);
}
void deallocate(pointer p, size_type n)
{
fprintf(stderr, "Dealloc %d bytes (%p).\n", n, p);
alloc -= n;
return std::allocator<T>::deallocate(p, n);
}
mmap_allocator() throw(): std::allocator<T>() { fprintf(stderr, "Hello allocator!\n"); }
mmap_allocator(const mmap_allocator &a) throw(): std::allocator<T>(a) { }
~mmap_allocator() throw() { }
};
int main(){
std::vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());
std::map<int, int, std::less<int>, mmap_allocator<std::pair<int,int> > > x;
x[1] = 2;
printf("s=%lu\n", alloc);
return 0;
}
Linux, gcc 4.4.6.
I haven't tried fixing it but it seems you haven't defined a constructor which takes an allocator instantiation with a different template argument. That is, you are missing something like
template <typename T>
template <typename O>
mmap_allocator<T>::mmap_allocator(mmap_allocator<O> const& other) {
...
}
From the looks of it, the error stems from trying to construct an allocator type obtained from rebind with some other allocator.
I'm trying to jump through some hoops to organize data in a special way. I'm including a simplified piece of code that demonstrates my pain.
I can't use boost.
I'm using the latest version of g++ in cygwin.
#include <iostream>
#include <map>
using namespace std;
int main () {
map< int,int > genmap;
map< int,int >::iterator genmapit;
map< map<int,int>::iterator,int > itermap;
// insert something into genmap
genmap.insert (make_pair(1,500) );
// find and return iterator.
genmapit=genmap.find(1);
// insert the iterator/int into itermap. Dies on each of the following 3 versions of this line.
//itermap[genmapit] = 600; // crash
//itermap.insert ( pair< map<int,int>::iterator,int >(genmapit,600) ); // crash
itermap.insert ( make_pair(genmapit,600) ); // crash
return 0;
}
So as you can see, I have 1 simple map, an iterator to that map and another map that has the first argument as an iterator to the first map.
It's clear from this:
Why can't I put an iterator in map?
That I can have an iterator as the second argument. However, the way shown above provides this:
$ make
g++ -c -o main.o main.cpp
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h: In member fun
ction `bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp =
std::_Rb_tree_iterator<std::pair<const int, int> >]':
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_tree.h:871: instantiate
d from `std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _All
oc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::i
nsert_unique(const _Val&) [with _Key = std::_Rb_tree_iterator<std::pair<const in
t, int> >, _Val = std::pair<const std::_Rb_tree_iterator<std::pair<const int, in
t> >, int>, _KeyOfValue = std::_Select1st<std::pair<const std::_Rb_tree_iterator
<std::pair<const int, int> >, int> >, _Compare = std::less<std::_Rb_tree_iterato
r<std::pair<const int, int> > >, _Alloc = std::allocator<std::pair<const std::_R
b_tree_iterator<std::pair<const int, int> >, int> >]'
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_map.h:360: instantiated
from `std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_
Select1st<std::pair<const _Key, _Tp> >, _Compare, _Alloc>::iterator, bool> std::
map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [wit
h _Key = std::_Rb_tree_iterator<std::pair<const int, int> >, _Tp = int, _Compare
= std::less<std::_Rb_tree_iterator<std::pair<const int, int> > >, _Alloc = std:
:allocator<std::pair<const std::_Rb_tree_iterator<std::pair<const int, int> >, i
nt> >]'
main.cpp:23: instantiated from here
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h:227: error: no
match for 'operator<' in '__x < __y'
make: *** [main.o] Error 1
"instantiated from here" tells me nothing and a web search gives me no info on this.
Does STL:map simply not allow for this? I can recode my app to work around this but it will be very inefficient and I would like to get this working. Is there another kind of pointer I can make for a map element I could use?
Thanks for your time.
You can't do this because std::map iterators are not random access iterators so aren't comparable with <.
Instead, you could use pointers to the value_type in the first map as a map key.
You have to learn to read the error messages. In particular look at the message that comes after the long-winded description where the error happened:
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h:227: error: no
match for 'operator<' in '__x < __y'
Map iterators are not comparable with less-than operator which the map uses by default.
I suppose you can provide a comparison function that compares the pairs pointed to by the iterator, since the iterators themselves cannot be easily compared in a meaningful way.
struct CompareIterator
{
template <class FirstIter, class SecondIter>
bool operator()(FirstIter lhv, SecondIter rhv) const
{
return *lhv < *rhv;
}
};
//usage with map:
map< map<int,int>::iterator,int, CompareIterator > itermap;
std::pair defines operator<. I also used two iterator types, since it might be possible the types are different (iterator and const_iterator)
map<Key, Value>
The map iterator as key element into another map is not possible because map expects operator < to be defined by default to the key. If the Key (in this case map iterator) is not defined then you need to pass a functor as a predicate function that provides the comparison of Key (map iterator).