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 ma开发者_开发百科tching 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 ); } );
精彩评论