Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use STL copy function with map

Tags:

c++

copy

map

stl

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.

like image 760
systemsfault Avatar asked Jun 01 '11 11:06

systemsfault


People also ask

Does map insert make a copy?

Yes -- when you insert an item into an std::map, you pass it by value, so what it contains is a copy of what you passed.

Can we copy one map to another in C++?

Starting with C++17, we can use the std::map::merge member function to copy content from a map to another map. This is the recommended way to copy the contents of a map to an existing map in C++17 and above. That's all about copying entries of a map to another map in C++.


4 Answers

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.

like image 54
Oliver Charlesworth Avatar answered Nov 14 '22 03:11

Oliver Charlesworth


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.

like image 21
James Kanze Avatar answered Nov 14 '22 02:11

James Kanze


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;
}
like image 39
Puppy Avatar answered Nov 14 '22 01:11

Puppy


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.)

like image 29
Lightness Races in Orbit Avatar answered Nov 14 '22 02:11

Lightness Races in Orbit