La prima volta che viene chiamato quando viene passato al filtro - durante l'incremento.
La seconda volta che viene chiamato nel vostro intervallo basato su - durante il trasferimento. Non memorizza i risultati nella cache.
cioè solo di passaggio gamma:
++++++++++boost::begin(s);
gives:
fun: 1
fun: 2
fun: 3
fun: 4
fun: 5
fun: 6
fun: 7
fun: 8
fun: 9
fun: 10
Verifica attuazione filter_iterator (filtrato è basata su esso). Non fa alcun caching.
Cosa succede se la trasformazione è costosa?
filtrato non utilizzare knowladge da cui proviene l'input.
La memorizzazione nella cache del risultato richiederebbe la dimensione crescente degli iteratori filtrati. Basti pensare a dove deve essere memorizzato il risultato della cache. Dovrebbe essere copiato in qualche membro di iteratore filtrato.
Quindi, fondamentalmente, c'è un compromesso tra lo spazio per il caching e il conteggio del dereferenziamento.
EDIT: ho fatto proof-of-concept di cached_iterator che memorizza nella cache risultato di dereference, e invalida su ogni avanzamento. Inoltre, ho realizzato un adattatore per campo corrispondente.
Ecco come viene utilizzato:
auto s = v | transformed(only_even) | cached | reversed | cached | flt | flt | flt | flt | flt | flt;
È necessario posizionare cache a catena in cui si desidera memorizzare nella cache risultato.
live demo
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <ostream>
// ____________________________________________________________________________________________ //
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/range/iterator.hpp>
#include <iterator>
namespace impl
{
template<typename Iterator>
class cached_iterator : public boost::iterator_adaptor<cached_iterator<Iterator>,Iterator>
{
typedef boost::iterator_adaptor<cached_iterator,Iterator> super;
mutable bool invalidated;
mutable typename std::iterator_traits<Iterator>::value_type cached;
public:
cached_iterator() : invalidated(true) {}
cached_iterator(const Iterator &x) : super(x), invalidated(true) {}
typename std::iterator_traits<Iterator>::value_type dereference() const
{
if(invalidated)
{
cached = *(this->base());
invalidated=false;
return cached;
}
else
{
return cached;
}
}
void increment()
{
invalidated=true;
++(this->base_reference());
}
void decrement()
{
invalidated=true;
--(this->base_reference());
}
void advance(typename super::difference_type n)
{
invalidated=true;
(this->base_reference())+=n;
}
};
template<typename Iterator> cached_iterator<Iterator> make_cached_iterator(Iterator it)
{
return cached_iterator<Iterator>(it);
}
template< class R >
struct cached_range : public boost::iterator_range<cached_iterator<typename boost::range_iterator<R>::type> >
{
private:
typedef boost::iterator_range<cached_iterator<typename boost::range_iterator<R>::type> > base;
public:
typedef R source_range_type;
cached_range(R& r)
: base(make_cached_iterator(boost::begin(r)), make_cached_iterator(boost::end(r)))
{ }
};
template<typename InputRange>
inline cached_range<const InputRange> cache(const InputRange& rng)
{
return cached_range<const InputRange>(rng);
}
template<typename InputRange>
inline cached_range<InputRange> cache(InputRange& rng)
{
return cached_range<InputRange>(rng);
}
struct cache_forwarder{};
cache_forwarder cached;
template< class InputRange >
inline cached_range<const InputRange>
operator|(const InputRange& r, cache_forwarder)
{
return cache(r);
}
template< class InputRange >
inline cached_range<InputRange>
operator|(InputRange& r, cache_forwarder)
{
return cache(r);
}
} // namespace impl
// ____________________________________________________________________________________________ //
struct OnlyEven
{
typedef int argument_type;
typedef std::pair<bool, int> result_type;
result_type operator()(argument_type x) const
{
std::cout << "fun: " << x << std::endl;
return std::make_pair(x % 2 == 0, x);
}
} only_even;
int main()
{
using namespace impl;
using namespace boost::adaptors;
using namespace std;
int v[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto flt = filtered([](std::pair<bool, int> x)->bool{ return x.first; });
auto s = v | transformed(only_even) | cached | reversed | cached | flt | flt | flt | flt | flt | flt;
boost::copy(s | map_values, ostream_iterator<int>(cout,"\n"));
return 0;
}
uscita è:
fun: 10
10
fun: 9
fun: 8
8
fun: 7
fun: 6
6
fun: 5
fun: 4
4
fun: 3
fun: 2
2
fun: 1
Sì, ho appena capito che fuori dopo aver guardato attraverso il codice. Questo ha senso per 'transform', chiamando' fun' due volte? Pensare ad altri linguaggi (Python, Haskell, ecc. Non ha senso). Cosa succede se la trasformazione è costosa? –
In tal caso, è possibile utilizzare l'adattatore "cache" come mostrato nella risposta. –
Grazie per l'adattatore della cache! Risolve il problema :) –