2008-11-03 216 views
15

我刚刚回到C++后,做了很多C#,并且最近Objective C迭代器适配器只迭代地图中的值?

我以前做过的一件事是将我自己的迭代器适配器转换为std :: map这将只涉及价值部分,而不是关键值对。这是很常见和自然的事情。 C#为这个工具提供了它的Dictionary类的Keys和Values属性。 Objective-C的NSDictionary同样拥有allKeys和allValue。

由于我已经“离开”,Boost已经获得了Range和ForEach库,我现在正在广泛使用它。我想知道在两者之间是否有一些设施可以做同样的事情,但我一直没能找到任何东西。

我正在考虑用Boost的迭代器适配器敲一些东西,但在我走下那条路线之前,我想我会问在这里,如果有人知道Boost中的这样一个设施,或者其他地方准备好了吗?

+0

你可能要检查这个答案:http://stackoverflow.com/questions/2311752/boost-bind-to-access-stdmap- elements-in-stdfor-each/2312015#2312015 – Manuel 2010-02-23 08:20:17

回答

14

我不认为有什么开箱即用的。你可以使用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") 
    ); 
} 
+2

谢谢大卫。这和我之前做的非常相似。它仍然需要很多我喜欢的样板。尽管投票你证明一个完全有效的答案。仍然希望获得更多... – philsquared 2008-11-03 17:48:57

+0

请注意,在函数模板中将`boost :: make_transform_iterator(Map :: const_iterator,take_second < Key, Value>)`封装起来并不重要。这使得boile – MSalters 2009-12-14 08:36:53

7

继续David的回答,还有另一种可能性,通过从boost :: transform_iterator创建派生类来放置boile。我使用这个解决方案在我的项目:

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

有一个升压范围适配器正是为了这个目的。 见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; 
}