Fill std::map with std::generate_n - c++

I'd like to fill a std::map using std::generate_n but can't get it to work. What I tried is something along these lines:
unsigned number_of_pairs{5};
std::map<std::string, std::string> my_map;
auto read_pair_from_input = [](){
std::string key;
std::getline(std::cin, key);
std::string value;
std::getline(std::cin, value);
return std::make_pair(key, value);
};
std::generate_n(my_map.begin(), number_of_pairs, read_pair_from_input);
This gives me long errors like:
In file included from /opt/wandbox/gcc- head/include/c++/8.0.0/algorithm:62:0,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h: In instantiation of '_OIter std::generate_n(_OIter, _Size, _Generator) [with _OIter = std::_Rb_tree_iterator<std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; _Size = unsigned int; _Generator = main()::<lambda()>]':
prog.cc:18:74: required from here
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h:4468:11: error: use of deleted function 'std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator= (typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type) [with _T1 = const std::__cxx11::basic_string<char>; _T2 = std::__cxx11::basic_string<char>; typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type = const std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&]'
*__first = __gen();
~~~~~~~~~^~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/8.0.0/utility:70:0,
from /opt/wandbox/gcc-head/include/c++/8.0.0/algorithm:60,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_pair.h:378:7: note: declared here
operator=(typename conditional<
^~~~~~~~
Is it possible to fill a std::map with std::generate_n?

What you want is a std::inserter:
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);
The inserter will wrap your map into an iterator-like construct that std::generate_n can use
Demo

std::generate_n can be implemented like
template< class OutputIt, class Size, class Generator >
OutputIt generate_n( OutputIt first, Size count, Generator g )
{
for( Size i = 0; i < count; i++ ) {
*first++ = g();
}
return first;
}
As you can see it tries to assign the result of the generator to the iterator. This does not work with associative containers as you cannot modify the key as that would break the structure of the container.
What you need is different type of iterator, namely a std::insert_iterator that you can get using std::inserter like
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);

Related

std::set_difference on map and a vector throws conversion errors

The following code should compute the difference of a map and a vector
std::map<int, int> cursorMap;
QVector<User> userList;
...
std::vector<int> offlineUserIds{};
std::vector<int>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(),
[](const std::pair<int, int> &e, const User &u){ return u.getId() == e.first; });
before invoking set_difference, userList is converted to a std::vector and sorted. The problem is that it gaves me the following errors:
error: cannot convert 'std::pair<const int, int>' to 'int' in assignment
...
error: no match for call to '(TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>) (User&, std::pair<const int, int>&)'
{ return bool(_M_comp(*__it1, *__it2)); }
...
note: candidate: 'TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>'
...
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return u.getId() == e.first; });
...
note: no known conversion for argument 1 from 'User' to 'const std::pair<int, int>&'
EDIT:
I tried the following code
std::map<int, int> cursorMap;
QVector<User> userList;
...
std::vector<std::pair<int, int>> offlineUserIds{};
std::vector<std::pair<int, int>>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return e.first < u.getId(); });
but now it gives me
note: candidate: 'TextEditor::updateCursorMap(QVector<User>)::<lambda(const std::pair<int, int>&, const User&)>'
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](const std::pair<int, int> &e, const User &u){ return e.first < u.getId(); });
...
note: no known conversion for argument 1 from 'User' to 'const std::pair<int, int>&'
EDIT2:
Here is a minimal reproducible example:
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
class User {
int id;
public:
int getId() const { return id; }
};
int main() {
std::vector<User> newUserList{};
std::map<int, int> cursorMap{};
std::vector<User> userList = std::vector(newUserList.begin(), newUserList.end());
std::sort(userList.begin(), userList.end(), [](const User &u1, const User &u2) { return u1.getId() < u2.getId(); });
std::vector<std::pair<int, int>> offlineUserIds{};
std::vector<std::pair<int, int>>::iterator it;
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](std::pair<int, int> e, User u){ return e.first < u.getId(); });
return 0;
}
build output
In file included from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algobase.h:71,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/char_traits.h:39,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/ios:40,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/ostream:38,
from C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/iostream:39,
from D:\asant\workspace\CLionProjects\untitled\main.cpp:1:
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h: In instantiation of 'constexpr bool __gnu_cxx::__ops::_Iter_comp_iter<_Compare>::operator()(_Iterator1, _Iterator2) [with _Iterator1 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _Iterator2 = std::_Rb_tree_iterator<std::pair<const int, int> >; _Compare = main()::<lambda(std::pair<int, int>, User)>]':
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algo.h:5343:17: required from '_OutputIterator std::__set_difference(_InputIterator1, _InputIterator1, _InputIterator2, _InputIterator2, _OutputIterator, _Compare) [with _InputIterator1 = std::_Rb_tree_iterator<std::pair<const int, int> >; _InputIterator2 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _OutputIterator = __gnu_cxx::__normal_iterator<std::pair<int, int>*, std::vector<std::pair<int, int> > >; _Compare = __gnu_cxx::__ops::_Iter_comp_iter<main()::<lambda(std::pair<int, int>, User)> >]'
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/stl_algo.h:5447:46: required from '_OIter std::set_difference(_IIter1, _IIter1, _IIter2, _IIter2, _OIter, _Compare) [with _IIter1 = std::_Rb_tree_iterator<std::pair<const int, int> >; _IIter2 = __gnu_cxx::__normal_iterator<User*, std::vector<User> >; _OIter = __gnu_cxx::__normal_iterator<std::pair<int, int>*, std::vector<std::pair<int, int> > >; _Compare = main()::<lambda(std::pair<int, int>, User)>]'
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:188: required from here
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: error: no match for call to '(main()::<lambda(std::pair<int, int>, User)>) (User&, std::pair<const int, int>&)'
{ return bool(_M_comp(*__it1, *__it2)); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: note: candidate: 'bool (*)(std::pair<int, int>, User)' <conversion>
C:/Qt/Tools/mingw810_64/lib/gcc/x86_64-w64-mingw32/8.1.0/include/c++/bits/predefined_ops.h:143:18: note: candidate expects 3 arguments, 3 provided
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:156: note: candidate: 'main()::<lambda(std::pair<int, int>, User)>'
it = std::set_difference(cursorMap.begin(), cursorMap.end(), userList.begin(), userList.end(), offlineUserIds.begin(), [](std::pair<int, int> e, User u){ return e.first < u.getId(); });
^
D:\asant\workspace\CLionProjects\untitled\main.cpp:21:156: note: no known conversion for argument 1 from 'User' to 'std::pair<int, int>'
mingw32-make.exe[3]: *** [CMakeFiles\untitled.dir\build.make:82: CMakeFiles/untitled.dir/main.cpp.obj] Error 1
mingw32-make.exe[2]: *** [CMakeFiles\Makefile2:95: CMakeFiles/untitled.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:102: CMakeFiles/untitled.dir/rule] Error 2
mingw32-make.exe: *** [Makefile:137: untitled] Error 2
For a reason that is not known to me:
The types Type1 and Type2 must be such that objects of types InputIt1
and InputIt2 can be dereferenced and then implicitly converted to both
Type1 and Type2
See cppreference. Last sentence under Parameters -> comp. Highlighted by me.
The value type of the template class std::map is defined like
typedef pair<const Key, T> value_type;
However the value type of the class std::vector<int> is int.
You can not assign an object of the typs std::pair to an object of the type int.
So the compiler issues an error.
Moreover the comparison function can be called with any order of the passed arguments. So again the compiler can issue an error that invalid arguments are used because the types of parameters are different and there is no implicit conversion from one type to another..
You may want either roll own difference function or provide User with conversion operator which would allow set_difference to compare pair and User.
set_difference requires mutual conversion and ability to insert Type1 to output sequence. With use of std::map mutual conversion is not possible because conversion operator must be member of std::pair in that case.
The comparator cmp for set_difference is LessCompare and the equivalence will be checked with
something like:
pair<int, int> e == User u <==> !(e < u) && !(u < e) <==> !cmp(e,u) && !cmp(u,e)
This will work only if e and u are convertible to each other, which is not the case.
I believe your lambda is taking parameters in the wrong order. Try the one below.
[](const User &u, const std::pair<int, int> &e){ return u.getId() == e.first; });

Usage of multimap with next_permutation c++

i am just trying to implement Knapsack Problem in a Naive way to stress test my original solution.
MY CODE
double StressTest(multimap<int, int> ValWt, int KnapscakWeight)
{
vector<double> TotalValue;
double Temp_KnapsackWeight = 0.0;
double Value = 0.0;
multimap<int, int>::iterator itr1;// = ValWt.begin();
do
{
itr1 = ValWt.begin();
Temp_KnapsackWeight = KnapscakWeight;
while( (Temp_KnapsackWeight > 0) && (itr1 != ValWt.end()) )
{
if(itr1->second > Temp_KnapsackWeight)
{
Value += ( (Temp_KnapsackWeight/itr1->second) * itr1->first );
Temp_KnapsackWeight = 0;
}
else
{
Temp_KnapsackWeight -= itr1->second;
Value += itr1->first;
}
itr1++;
}
TotalValue.push_back(Value);
Value = 0.0;
}while( next_permutation(ValWt.begin(), ValWt.end()) );
return *max_element(TotalValue.begin(), TotalValue.end());
}
ERROR
In file included from /usr/include/c++/7/bits/char_traits.h:39:0,
from /usr/include/c++/7/ios:40,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from 2_max_val_of_loot.cpp:1:
/usr/include/c++/7/bits/stl_algobase.h: In instantiation of ‘void std::iter_swap(_ForwardIterator1, _ForwardIterator2) [with _ForwardIterator1 = std::_Rb_tree_iterator<std::pair<const int, int> >; _ForwardIterator2 = std::_Rb_tree_iterator<std::pair<const int, int> >]’:
/usr/include/c++/7/bits/stl_algo.h:2926:22: required from ‘bool std::__next_permutation(_BidirectionalIterator, _BidirectionalIterator, _Compare) [with _BidirectionalIterator = std::_Rb_tree_iterator<std::pair<const int, int> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]’
/usr/include/c++/7/bits/stl_algo.h:2966:2: required from ‘bool std::next_permutation(_BIter, _BIter) [with _BIter = std::_Rb_tree_iterator<std::pair<const int, int> >]’
2_max_val_of_loot.cpp:39:53: required from here
/usr/include/c++/7/bits/stl_algobase.h:148:11: error: use of deleted function ‘typename std::enable_if<(! std::__and_<std::__is_swappable<_T1>, std::__is_swappable<_T2> >::value)>::type std::swap(std::pair<_T1, _T2>&, std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = int; typename std::enable_if<(! std::__and_<std::__is_swappable<_T1>, std::__is_swappable<_T2> >::value)>::type = void]’
swap(*__a, *__b);
~~~~^~~~~~~~~~~~
In file included from /usr/include/c++/7/bits/stl_algobase.h:64:0,
from /usr/include/c++/7/bits/char_traits.h:39,
from /usr/include/c++/7/ios:40,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from 2_max_val_of_loot.cpp:1:
/usr/include/c++/7/bits/stl_pair.h:503:5: note: declared here
swap(pair<_T1, _T2>&, pair<_T1, _T2>&) = delete;
MY OBSERVATION
next_permutation() is creating error, but why i don't understand why.
next_permutation() requires bidirectional iterator and multimap iterator is a bidirectional iterator.
I doubt as multimap are sorted always, that's why the error is shown ??
Please Help.
Thanking You.
You cannot use std::map or std::multimap (or unordered versions) as std::next_permutation requires:
-BidirIt must meet the requirements of ValueSwappable and LegacyBidirectionalIterator.
but std::multimap values are not swappable as key in the map is not mutable:
value_type std::pair<const Key, T>
(emphasis is mine)
Less formally order of elements in map is determined and cannot me changed. You have to use different container like std::vector to perform this operation.

Wrapping unordered_map to build an unmodifiable (immutable) map

I am playing with container types, gettingmore into the details and try to build an unmodifiable (or immutable) map.
For that, I built some kind like copy-constructor to add all elements from another unordered_map into my UnmodifiableMap (which is basically a wrapper for std::unordered_map). To make it unmodifiable, I only provide const iterators and read-only methods. But I get stuck with the constructor, I am sure I miss something, maybe somebody can point me the problem here. Maybe it is a totally wrong way like this, but his is what tried so far:
template <typename Key, typename T,
typename HashFcn = std::hash<Key>,
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator<std::pair<const Key, T> > >
class UnmodifiableMap {
public:
// The actual data
typedef std::unordered_map<T, Key, HashFcn, EqualKey> base;
typedef Key key_type;
typedef T data_type;
typedef T mapped_type;
typedef std::pair<const key_type, data_type> value_type;
typedef HashFcn hasher;
typedef EqualKey key_equal;
typedef Alloc allocator_type;
typedef typename base::size_type size_type;
typedef typename base::const_iterator const_iterator;
typedef typename base::iterator iterator;
private:
base _map;
/**
* Constructs an empty unordered_map
*/
UnmodifiableMap(
size_type n = 0, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type &alloc = allocator_type())
: _map(n, hf, eql, alloc) {}
public:
/** Constructs a copy of unordered_map */
UnmodifiableMap(const base& other)
: _map(static_cast<const base&>(other)) {}
~UnmodifiableMap() {}
iterator begin() { return _map.begin(); }
iterator end() { return _map.end(); }
const_iterator begin() const { return _map.begin(); }
const_iterator end() const { return _map.end(); }
bool empty() const { return _map.empty(); }
bool contains(const key_type& key) const {
return _map.find(key) != _map.end(); }
};
And here the main body:
int main(int argc, char **argv) {
typedef std::unordered_map<int, std::string> Items;
Items map;
map[1] = "first string";
map[4] = "string 4";
map[5] = "string 5";
map[22] = "string 22";
map[12] = "string 12";
map[18] = "string 18";
typedef UnmodifiableMap<int, std::string> ReadOnlyItems;
ReadOnlyItems readonlymap(map);
return 0;
}
The error I get is
Unmodifiable_map.cpp: In function ‘int main(int, char**)’:
Unmodifiable_map.cpp:56:25: error: no matching function for call to ‘UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(Items&)’
Unmodifiable_map.cpp:56:25: note: candidates are:
Unmodifiable_map.h:45:2: note: UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::UnmodifiableMap(const base&) [with Key = int, T = std::basic_string<char>, HashFcn = std::hash<int>, EqualKey = std::equal_to<int>, Alloc = std::allocator<std::pair<const int, std::basic_string<char> > >, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::base = std::unordered_map<std::basic_string<char>, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const std::basic_string<char>, int> > >]
Unmodifiable_map.h:45:2: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘const base& {aka const std::unordered_map<std::basic_string<char>, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const std::basic_string<char>, int> > >&}’
Unmodifiable_map.h:37:2: note: UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::UnmodifiableMap(UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with Key = int, T = std::basic_string<char>, HashFcn = std::hash<int>, EqualKey = std::equal_to<int>, Alloc = std::allocator<std::pair<const int, std::basic_string<char> > >, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::size_type = long unsigned int, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::hasher = std::hash<int>, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::key_equal = std::equal_to<int>, UnmodifiableMap<Key, T, HashFcn, EqualKey, Alloc>::allocator_type = std::allocator<std::pair<const int, std::basic_string<char> > >]
Unmodifiable_map.h:37:2: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘long unsigned int’
Unmodifiable_map.h:14:7: note: UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(const UnmodifiableMap<int, std::basic_string<char> >&)
Unmodifiable_map.h:14:7: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘const UnmodifiableMap<int, std::basic_string<char> >&’
Unmodifiable_map.h:14:7: note: UnmodifiableMap<int, std::basic_string<char> >::UnmodifiableMap(UnmodifiableMap<int, std::basic_string<char> >&&)
Unmodifiable_map.h:14:7: note: no known conversion for argument 1 from ‘Items {aka std::unordered_map<int, std::basic_string<char> >}’ to ‘UnmodifiableMap<int, std::basic_string<char> >&&’
Hope somebody can shed some light on that. Also I think I need to do more in the copy constructor, propably copy the elements (like a swap function) and an implementaton of operator= ?!
You are swapping the role of T and Key in your base type definition inside your wrapper class:
template <typename Key, typename T,
typename HashFcn = std::hash<Key>,
typename EqualKey = std::equal_to<Key>,
typename Alloc = std::allocator<std::pair<const Key, T> > >
class UnmodifiableMap {
public:
// typedef std::unordered_map<T, Key, HashFcn, EqualKey> base; // ERROR
typedef std::unordered_map<Key, T, HashFcn, EqualKey> base; // OK
...
Hence, your underlying map ends up being an unordered_map<string, int> rather than an unordered_map<int, string>, and the compiler complains about the mismatch.
Also notice, that you have an unnecessary static_cast in your constructor:
UnmodifiableMap(const base& other)
// : _map(static_cast<const base&>(other)) {} // NOT NEEDED. Just do:
: _map(other) {}

Is it possible to use STL copy function with map

I wonder if there is any trick to use copy with maps to copy the contents of map into an array. Because STL maps are by the combination of a key value and a mapped value an element of a map forms a key value pair. That prevents us to use standard algorithms like std::copy. For example following code gives error:
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
int
main()
{
std::map <int, double> test(4);
test[0] = 11;
test[2] = 1.23;
test[3] = 23.29;
test[1] = 12.12;
double *test_arr = (double *) malloc(4 * sizeof(double));
std::copy(test.begin(), test.end(), test_arr);
std::cout << test_arr[3] << std::endl;
return 0;
}
Error:
stl_copy_tests.cpp: In function ‘int main()’:
stl_copy_tests.cpp:9:32: error: no matching function for call to ‘std::map<int, double>::map(int)’
/usr/include/c++/4.5/bits/stl_map.h:170:7: note: candidates are: std::map<_Key, _Tp, _Compare, _Alloc>::map(const std::map<_Key, _Tp, _Compare, _Alloc>&) [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >, std::map<_Key, _Tp, _Compare, _Alloc> = std::map<int, double>]
/usr/include/c++/4.5/bits/stl_map.h:159:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::map(const _Compare&, const allocator_type&) [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >, allocator_type = std::allocator<std::pair<const int, double> >]
/usr/include/c++/4.5/bits/stl_map.h:150:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::map() [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >]
In file included from /usr/include/c++/4.5/bits/char_traits.h:41:0,
from /usr/include/c++/4.5/ios:41,
from /usr/include/c++/4.5/ostream:40,
from /usr/include/c++/4.5/iostream:40,
from stl_copy_tests.cpp:1:
/usr/include/c++/4.5/bits/stl_algobase.h: In static member function ‘static _OI std::__copy_move<<anonymous>, <anonymous>, <template-parameter-1-3> >::__copy_m(_II, _II, _OI) [with _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*, bool <anonymous> = false, bool <anonymous> = false, <template-parameter-1-3> = std::bidirectional_iterator_tag]’:
/usr/include/c++/4.5/bits/stl_algobase.h:404:70: instantiated from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false, _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
/usr/include/c++/4.5/bits/stl_algobase.h:442:39: instantiated from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false, _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
/usr/include/c++/4.5/bits/stl_algobase.h:474:18: instantiated from ‘_OI std::copy(_II, _II, _OI) [with _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
stl_copy_tests.cpp:15:47: instantiated from here
/usr/include/c++/4.5/bits/stl_algobase.h:319:6: error: cannot convert ‘std::pair<const int, double>’ to ‘double’ in assignment
Is there any easy trick/hack to overcome this problem.
Disclaimer: Not interested in solutions that iterates over map in a for loop and adds elements to the array.
You could use std::transform instead:
template <typename T, typename U>
const U &extract_second(const std::pair<T,U> &p)
{
return p.second;
}
std::transform(test.begin(), test.end(), test_arr, extract_second<int,double>);
And as #Andre points out in a comment below, if you want a slightly more verbose overhead, you can avoid having to explicitly state the template arguments via a functor:
struct extract_second
{
template <typename T, typename U>
const U operator() (const std::pair<T,U> &p) const
{
return p.second;
}
};
std::transform(test.begin(), test.end(), test_arr, extract_second());
I'm sure there's a less-verbose solution using Boost binders, but I can't remember the syntax off the top of my head.
Ewww, malloc? Anyway, if you want to copy a map, you have to remember the keys too.
int main()
{
std::map <int, double> test(4);
test[0] = 11;
test[2] = 1.23;
test[3] = 23.29;
test[1] = 12.12;
std::vector<std::pair<int, double>> test_arr(test.size());
std::copy(test.begin(), test.end(), test_arr.begin());
std::cout << test_arr[3] << std::endl;
return 0;
}
If you consider std::map an STL container, then it is a container of
std::pair<key_type, mapped_type>. (This is what its value_type is
defined to be, and it is designed so that it can be used as a
container.) If you want simply one part of it, the correct function is
std::transform, with a transformation function which maps the
value_type to either the key_type or the mapped_type. (If you make
much use of std::pair—or std::map, whose value_type is an
std::pair, you should probably have functional objects for this in
your tool kit:
struct ExtractFirst
{
template<typename Pair>
typename boost::remove_const<typename Pair::first_type>::type
operator()( Pair const& from ) const
{
return from.first;
}
};
, and the same thing for ExtractSecond.
Your target would be an arraystd::vector[please!] of std::pair<int,double> objects unless, yes, you unroll it yourself.
(You could create your own InputIterator as a proxy, or play with std::transform and a std::back_inserter, but that's just being silly. You'll make your code far more verbose than just looping through the map.)
The simplest way is to use std::transform in combination with boost::bind:
typedef std::map<int, double> map_t;
map_t mm;
// add elements to mm
// ...
// copy
typedef std::vector<double> vec_t;
vec_t vv;
vv.reserve( mm.size() );
std::transform( mm.begin(), mm.end(), std::back_inserter(vv),
boost::bind( &map_t::value_type::second, _1 ) );
If you could use C++0x (without boost):
std::transform( mm.begin(), mm.end(), back_inserter(vv),
[](map_t::value_type val) -> double { return val.second; } );
// or
std::for_each( mm.begin(), mm.end(),
[&vv](map_t::value_type val) { vv.push_back( val.second ); } );

Higher order function « filter » in C++

I wanted to write a higher order function filter with C++. The code I have come up with so far is as follows:
#include <iostream>
#include <string>
#include <functional>
#include <algorithm>
#include <vector>
#include <list>
#include <iterator>
using namespace std;
bool isOdd(int const i) {
return i % 2 != 0;
}
template <
template <class, class> class Container,
class Predicate,
class Allocator,
class A
>
Container<A, Allocator> filter(Container<A, Allocator> const & container, Predicate const & pred) {
Container<A, Allocator> filtered(container);
container.erase(remove_if(filtered.begin(), filtered.end(), pred), filtered.end());
return filtered;
}
int main() {
int const a[] = {23, 12, 78, 21, 97, 64};
vector<int const> const v(a, a + 6);
vector<int const> const filtered = filter(v, isOdd);
copy(filtered.begin(), filtered.end(), ostream_iterator<int const>(cout, " "));
}
However on compiling this code, I get the following error messages that I am unable to understand and hence get rid of:
/usr/include/c++/4.3/ext/new_allocator.h: In instantiation of ‘__gnu_cxx::new_allocator<const int>’:
/usr/include/c++/4.3/bits/allocator.h:84: instantiated from ‘std::allocator<const int>’
/usr/include/c++/4.3/bits/stl_vector.h:75: instantiated from ‘std::_Vector_base<const int, std::allocator<const int> >’
/usr/include/c++/4.3/bits/stl_vector.h:176: instantiated from ‘std::vector<const int, std::allocator<const int> >’
Filter.cpp:29: instantiated from here
/usr/include/c++/4.3/ext/new_allocator.h:82: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(const _Tp&) const [with _Tp = const int]’ cannot be overloaded
/usr/include/c++/4.3/ext/new_allocator.h:79: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(_Tp&) const [with _Tp = const int]’
Filter.cpp: In function ‘Container<A, Allocator> filter(const Container<A, Allocator>&, const Predicate&) [with Container = std::vector, Predicate = bool ()(int), Allocator = std::allocator<const int>, A = const int]’:
Filter.cpp:30: instantiated from here
Filter.cpp:23: error: passing ‘const std::vector<const int, std::allocator<const int> >’ as ‘this’ argument of ‘__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> > std::vector<_Tp, _Alloc>::erase(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, __gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >) [with _Tp = const int, _Alloc = std::allocator<const int>]’ discards qualifiers
/usr/include/c++/4.3/bits/stl_algo.h: In function ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = __gnu_cxx::__normal_iterator<const int*, std::vector<const int, std::allocator<const int> > >, _Predicate = bool (*)(int)]’:
Filter.cpp:23: instantiated from ‘Container<A, Allocator> filter(const Container<A, Allocator>&, const Predicate&) [with Container = std::vector, Predicate = bool ()(int), Allocator = std::allocator<const int>, A = const int]’
Filter.cpp:30: instantiated from here
/usr/include/c++/4.3/bits/stl_algo.h:821: error: assignment of read-only location ‘__result.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = const int*, _Container = std::vector<const int, std::allocator<const int> >]()’
/usr/include/c++/4.3/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::deallocate(_Tp*, size_t) [with _Tp = const int]’:
/usr/include/c++/4.3/bits/stl_vector.h:150: instantiated from ‘void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(_Tp*, size_t) [with _Tp = const int, _Alloc = std::allocator<const int>]’
/usr/include/c++/4.3/bits/stl_vector.h:136: instantiated from ‘std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp = const int, _Alloc = std::allocator<const int>]’
/usr/include/c++/4.3/bits/stl_vector.h:286: instantiated from ‘std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const _Alloc&) [with _InputIterator = const int*, _Tp = const int, _Alloc = std::allocator<const int>]’
Filter.cpp:29: instantiated from here
/usr/include/c++/4.3/ext/new_allocator.h:98: error: invalid conversion from ‘const void*’ to ‘void*’
/usr/include/c++/4.3/ext/new_allocator.h:98: error: initializing argument 1 of ‘void operator delete(void*)’
/usr/include/c++/4.3/bits/stl_algobase.h: In function ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false, _II = const int*, _OI = const int*]’:
/usr/include/c++/4.3/bits/stl_algobase.h:435: instantiated from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false, _II = __gnu_cxx::__normal_iterator<const int*, std::vector<const int, std::allocator<const int> > >, _OI = __gnu_cxx::__normal_iterator<const int*, std::vector<const int, std::allocator<const int> > >]’
/usr/include/c++/4.3/bits/stl_algobase.h:466: instantiated from ‘_OI std::copy(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<const int*, std::vector<const int, std::allocator<const int> > >, _OI = __gnu_cxx::__normal_iterator<const int*, std::vector<const int, std::allocator<const int> > >]’
/usr/include/c++/4.3/bits/vector.tcc:136: instantiated from ‘__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> > std::vector<_Tp, _Alloc>::erase(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, __gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >) [with _Tp = const int, _Alloc = std::allocator<const int>]’
Filter.cpp:23: instantiated from ‘Container<A, Allocator> filter(const Container<A, Allocator>&, const Predicate&) [with Container = std::vector, Predicate = bool ()(int), Allocator = std::allocator<const int>, A = const int]’
Filter.cpp:30: instantiated from here
/usr/include/c++/4.3/bits/stl_algobase.h:396: error: no matching function for call to ‘std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m(const int*&, const int*&, const int*&)’
Please tell me what I am doing wrong here and what is the correct way to achieve the kind of higher order polymorphism I want.
Thanks.
EDIT:
Thank you, everyone. Here's the new code I got after applying your suggestions (and it works now, yay!)
#include <iostream>
#include <string>
#include <functional>
#include <algorithm>
#include <vector>
#include <list>
#include <iterator>
using namespace std;
bool isOdd(int const i) {
return i % 2 != 0;
}
template <
template <typename, typename> class Container,
typename Predicate,
typename Allocator,
typename A
>
Container<A, Allocator> filter(Container<A, Allocator> const & container, Predicate const & pred) {
Container<A, Allocator> filtered(container);
filtered.erase(remove_if(filtered.begin(), filtered.end(), pred), filtered.end());
return filtered;
}
int main() {
int a[] = {23, 12, 78, 21, 97, 64};
vector<int> v(a, a + 6);
vector<int> filtered = filter(v, isOdd);
copy(filtered.begin(), filtered.end(), ostream_iterator<int>(cout, " "));
}
Why is your Container parametrized at all?
template <typename C, typename P>
C filter(C const & container, P pred) {
C filtered(container);
filtered.erase(remove_if(filtered.begin(), filtered.end(), pred), filtered.end());
return filtered;
}
Works just as well. Notice that I passed P by value rather than by const reference, as advised by Meyers in Effective C++ (iterators and functors should be passed by value).
what about remove_copy_if instead ? (with isEven()). It's already built for you.
The error is not in filter, but in:
int main() {
int const a[] = {23, 12, 78, 21, 97, 64};
vector<int const> const v(a, a + 6);
}
You can't have a vector of const stuff. Remove the inner const:
int main() {
int const a[] = {23, 12, 78, 21, 97, 64};
vector<int> const v(a, a + 6);
}
(And of course, filtered.erase, not container.erase.)
container is a const-reference. You can't call erase() on it. You probably mean to call
filtered.erase(remove_if(filtered.begin(), filtered.end(), pred), filtered.end());