adattatore iteratore per iterare solo i valori in una mappa?
Domanda
Sto tornando al C ++ dopo un paio d'anni dopo aver fatto molto C # e recentemente l'Obiettivo C.
Una cosa che ho fatto prima è quella di creare il mio adattatore iteratore per std :: map che non verrà visualizzato solo nella parte valore, anziché nella coppia chiave-valore. Questa è una cosa abbastanza comune e naturale da fare. C # fornisce a questa funzione le sue proprietà Keys and Values ??della sua classe Dictionary. Allo stesso modo, NSDictionary di Objective-C ha allKeys e allValues.
Da quando sono stato "via", Boost ha acquisito le librerie Range e ForEach, che ora sto ampiamente usando. Mi chiedevo se tra i due ci fosse qualche possibilità di fare lo stesso, ma non sono riuscito a trovare nulla.
Sto pensando di buttare giù qualcosa usando gli adattatori iteratori di Boost, ma prima di percorrere quella strada ho pensato di chiedere qui se qualcuno fosse a conoscenza di una tale struttura a Boost o di un altro posto già pronto?
Soluzione
Non credo che ci sia niente fuori dagli schemi. Puoi usare 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")
);
}
Altri suggerimenti
Sostituendo la risposta precedente, nel caso in cui qualcun altro la trovi come ho fatto io. A partire dalla spinta 1.43, ci sono alcuni adattatori di gamma comunemente usati forniti. In questo caso, vuoi boost :: adapterters :: map_values. L'esempio pertinente: 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
Esiste un adattatore per boost range esattamente per questo scopo. Vedi http: //www.boost .org / doc / librerie / 1_53_0 / librerie / gamma / doc / html / gamma / riferimento / adattatori / riferimento / map_values.html
(Questo esempio è stato paralizzato da lì)
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;
}
Continuando la risposta di David, c'è un'altra possibilità di mettere il punto di ebollizione creando una classe derivata da boost :: transform_iterator. Sto usando questa soluzione nei miei progetti:
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)
{}
};
Questo porta a un codice ancora più pulito:
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")
);
}