代码之家  ›  专栏  ›  技术社区  ›  philsquared

迭代器适配器仅迭代映射中的值?

  •  18
  • philsquared  · 技术社区  · 17 年前

    在做了几年的C#和最近的Objective C之后,我刚刚回到C++。

    我之前做过的一件事是为std::map滚动我自己的迭代器适配器,它将只解映射到值部分,而不是键值对。这是一件非常常见和自然的事情。C#通过其Dictionary类的键和值属性提供了这种功能。Objective-C的NSDictionary同样具有所有键和所有值。

    自从我“离开”以来,Boost已经获得了Range和ForEach库,我现在正在广泛使用它们。我想知道在这两者之间是否有一些设施可以做同样的事情,但我什么也没找到。

    我在考虑使用Boost的迭代器适配器来开发一些东西,但在我走这条路之前,我想在这里问一下,是否有人知道Boost中的这种设施,或者其他现成的地方?

    4 回复  |  直到 17 年前
        1
  •  24
  •   Matt Chambers    15 年前

    替换之前的答案,以防其他人像我一样找到这个答案。从boost 1.43开始,提供了一些常用的量程适配器。在这种情况下,您需要boost::adaptors::map_values。相关示例: http://www.boost.org/doc/libs/1_46_0/libs/range/doc/html/range/reference/adaptors/reference/map_values.html#range.reference.adaptors.reference.map_values.map_values_example

        2
  •  14
  •   David Nehme    16 年前

    我认为没有什么是开箱即用的。您可以使用boost::make_transform。

    template<typename T1, typename T2> T2& take_second(const std::pair<T1, T2> &a_pair) 
    {
      return a_pair.second;
    }
    
    void run_map_value()
    {
      map<int,string> a_map;
      a_map[0] = "zero";
      a_map[1] = "one";
      a_map[2] = "two";
      copy( boost::make_transform_iterator(a_map.begin(), take_second<int, string>),
        boost::make_transform_iterator(a_map.end(), take_second<int, string>),
        ostream_iterator<string>(cout, "\n")
        );
    }
    
        3
  •  8
  •   Kibbick    13 年前

    有一个增压范围适配器正是为了这个目的。 看见 http://www.boost.org/doc/libs/1_53_0/libs/range/doc/html/range/reference/adaptors/reference/map_values.html

    (这个例子是从那里抄袭的)

    int main(int argc, const char* argv[])
    {
        using namespace boost::assign;
        using namespace boost::adaptors;
    
        std::map<int,int> input;
        for (int i = 0; i < 10; ++i)
        input.insert(std::make_pair(i, i * 10));
    
        boost::copy(
            input | map_values,
            std::ostream_iterator<int>(std::cout, ","));
    
        return 0;
    }
    
        4
  •  7
  •   klaus triendl    15 年前

    继续David的回答,还有另一种可能是通过从boost::transform_iterator创建派生类来实现样板。我在我的项目中使用了这个解决方案:

    namespace detail
    {
    
    template<bool IsConst, bool IsVolatile, typename T>
    struct add_cv_if_c
    {
        typedef T type;
    };
    template<typename T>
    struct add_cv_if_c<true, false, T>
    {
        typedef const T type;
    };
    template<typename T>
    struct add_cv_if_c<false, true, T>
    {
        typedef volatile T type;
    };
    template<typename T>
    struct add_cv_if_c<true, true, T>
    {
        typedef const volatile T type;
    };
    
    template<typename TestConst, typename TestVolatile, typename T>
    struct add_cv_if: public add_cv_if_c<TestConst::value, TestVolatile::value, T>
    {};
    
    }   // namespace detail
    
    
    /** An unary function that accesses the member of class T specified in the MemberPtr template parameter.
    
        The cv-qualification of T is preserved for MemberType
     */
    template<typename T, typename MemberType, MemberType T::*MemberPtr>
    struct access_member_f
    {
        // preserve cv-qualification of T for T::second_type
        typedef typename detail::add_cv_if<
            std::tr1::is_const<T>, 
            std::tr1::is_volatile<T>, 
            MemberType
        >::type& result_type;
    
        result_type operator ()(T& t) const
        {
            return t.*MemberPtr;
        }
    };
    
    /** @short  An iterator adaptor accessing the member called 'second' of the class the 
        iterator is pointing to.
     */
    template<typename Iterator>
    class accessing_second_iterator: public 
        boost::transform_iterator<
            access_member_f<
                // note: we use the Iterator's reference because this type 
                // is the cv-qualified iterated type (as opposed to value_type).
                // We want to preserve the cv-qualification because the iterator 
                // might be a const_iterator e.g. iterating a const 
                // std::pair<> but std::pair<>::second_type isn't automatically 
                // const just because the pair is const - access_member_f is 
                // preserving the cv-qualification, otherwise compiler errors will 
                // be the result
                typename std::tr1::remove_reference<
                    typename std::iterator_traits<Iterator>::reference
                >::type, 
                typename std::iterator_traits<Iterator>::value_type::second_type, 
                &std::iterator_traits<Iterator>::value_type::second
            >, 
            Iterator
        >
    {
        typedef boost::transform_iterator<
            access_member_f<
                typename std::tr1::remove_reference<
                    typename std::iterator_traits<Iterator>::reference
                >::type, 
                typename std::iterator_traits<Iterator>::value_type::second_type, 
                &std::iterator_traits<Iterator>::value_type::second
            >, 
            Iterator
        > baseclass;
    
    public:
        accessing_second_iterator(): 
            baseclass()
        {}
    
        // note: allow implicit conversion from Iterator
        accessing_second_iterator(Iterator it): 
            baseclass(it)
        {}
    };
    

    这导致了更清晰的代码:

    void run_map_value()
    {
      typedef map<int, string> a_map_t;
      a_map_t a_map;
      a_map[0] = "zero";
      a_map[1] = "one";
      a_map[2] = "two";
    
      typedef accessing_second_iterator<a_map_t::const_iterator> ia_t;
      // note: specify the iterator adaptor type explicitly as template type, enabling 
      // implicit conversion from begin()/end()
      copy<ia_t>(a_map.begin(), a_map.end(),
        ostream_iterator<string>(cout, "\n")
      );
    }