読者です 読者をやめる 読者になる 読者になる

boost::range::adaptors::map_keys, map_valuesを配列に適用できない

C++

std::mapやboost::unordered_mapからキーのRangeを生成するmap_keysと、
値のRangeを生成するmap_valuesですが、これらのRangeアダプタはpairのvectorなどにも適用することができます。

#include <vector>
#include <utility>
#include <boost/assert.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/equal.hpp>
#include <boost/assign/list_of.hpp>

int main()
{
    const std::vector<std::pair<int, char> > v = boost::assign::list_of
        (std::make_pair(3, 'a'))
        (std::make_pair(1, 'b'))
        (std::make_pair(4, 'c'));

    {
        const int expected[] = {3, 1, 4};
        BOOST_ASSERT(boost::equal(v | boost::adaptors::map_keys, expected));
    }

    {
        const char expected[] = { 'a', 'b', 'c' };
        BOOST_ASSERT(boost::equal(v | boost::adaptors::map_values, expected));
    }

    // mutable version
    {
        std::vector<std::pair<int, char> > mv(v.begin(), v.end());
        const char expected[] = { 'a', 'b', 'c' };
        BOOST_ASSERT(boost::equal(mv | boost::adaptors::map_values, expected));
    }
}

しかし、Boost 1.43.0時点でのmap_keys, map_valuesの実装が、Rangeに対してvalue_typeを持っていることを要求しているため、配列に対して適用することができません。

// <boost/range/adaptor/map.hpp>
template< class Map >
struct select_first
{
    typedef BOOST_DEDUCED_TYPENAME Map::value_type pair_t;
    typedef const BOOST_DEDUCED_TYPENAME pair_t::first_type& result_type;
    ...
};

template< class Map >
struct select_second_mutable
{
    typedef BOOST_DEDUCED_TYPENAME Map::value_type pair_t;
    typedef BOOST_DEDUCED_TYPENAME pair_t::second_type& result_type; 
    ...
};

template< class Map >
struct select_second_const
{
    typedef BOOST_DEDUCED_TYPENAME Map::value_type pair_t;
    typedef const BOOST_DEDUCED_TYPENAME pair_t::second_type& result_type;
    ...
};

これを以下のように修正すれば、配列に対しても適用できるようになります。

// <boost/range/adaptor/map.hpp>
template< class Map >
struct select_first
{
    typedef BOOST_DEDUCED_TYPENAME range_value<Map>::type pair_t;
    typedef const BOOST_DEDUCED_TYPENAME pair_t::first_type& result_type;
    ...
};

template< class Map > 
struct select_second_mutable 
{ 
    typedef BOOST_DEDUCED_TYPENAME range_value<Map>::type pair_t; 
    typedef BOOST_DEDUCED_TYPENAME pair_t::second_type& result_type; 
    ...
};

template< class Map >
struct select_second_const
{
    typedef BOOST_DEDUCED_TYPENAME range_value<Map>::type pair_t;
    typedef const BOOST_DEDUCED_TYPENAME pair_t::second_type& result_type;
    ...
};

以下、テストコードです。

#include <utility>
#include <boost/assert.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/equal.hpp>
#include <boost/range/algorithm/copy.hpp>

int main()
{
    const std::pair<int, char> ar[] = {
        std::make_pair(3, 'a'),
        std::make_pair(1, 'b'),
        std::make_pair(4, 'c')
    };

    {
        const int expected[] = {3, 1, 4};
        BOOST_ASSERT(boost::equal(ar | boost::adaptors::map_keys, expected));
    }

    {
        const char expected[] = { 'a', 'b', 'c' };
        BOOST_ASSERT(boost::equal(ar | boost::adaptors::map_values, expected));
    }

    // mutable version
    {
        std::pair<int, char> mar[3];
        boost::copy(ar, boost::begin(mar));

        const char expected[] = { 'a', 'b', 'c' };
        BOOST_ASSERT(boost::equal(mar | boost::adaptors::map_values, expected));
    }
}

これはバグだと思われるので、不具合報告(パッチ付き)しておきました。
https://svn.boost.org/trac/boost/ticket/4388