开发者

Is it possible to use STL copy function with map

开发者 https://www.devze.com 2023-03-09 14:06 出处:网络
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 ke

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

精彩评论

暂无评论...
验证码 换一张
取 消