2008-11-03 125 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; 
}