2013-03-15 18 views
9

Quali sono i vantaggi dell'utilizzo di boost::any_range? Ecco un esempio:Quali sono i vantaggi dell'utilizzo di boost :: any_range?

typedef boost::any_range< 
    int 
    , boost::forward_traversal_tag 
    , int 
    , std::ptrdiff_t 
> integer_range; 

void display_integers(const integer_range& rng) 
{ 
    boost::copy(rng, 
       std::ostream_iterator<int>(std::cout, ",")); 

    std::cout << std::endl; 
} 

int main(){ 
    std::vector<int> input{ ... }; 
    std::list<int> input2{ ... }; 
    display_integers(input); 
    display_integers(input2); 
} 

Ma la stessa funzionalità con maggiore efficienza può essere realizzato con un parametro di template, che soddisfa il concetto ForwardRange:

template <class ForwardRange> 
void display_integers(const ForwardRange& rng) 
{ 
    boost::copy(rng, 
       std::ostream_iterator<int>(std::cout, ",")); 

    std::cout << std::endl; 
} 

Così sto cercando per gli scenari in cui è vale la pena usare any_range. Forse mi manca qualcosa.

risposta

15

Questa tecnica è denominata Tipo di cancellazione. C'è un articolo completo che descrive i pro e i contro sull'esempio di any_iterator: On the Tension Between Object-Oriented and Generic Programming in C++.


E 'possibile nascondere implementazione/defintion di

void display_integers(const integer_range& rng) 

nel file/libreria separata.

Ma in caso di

template <class ForwardRange> 
void display_integers(const ForwardRange& rng) 

è necessario fornire il codice sorgente per gli utenti (o almeno fare explicit instantiations da qualche parte).


Inoltre, nel primo caso display_integers saranno forniti dati solo una volta, ma in seconda - sarà compilata per ogni tipo di gamma passata.


Inoltre, si può avere

integer_range rng; 

da qualche parte. E durante la vita del RNG - è possibile assegnare gamme di different types to it:

vector<int> v; 
list<int> l; 
integer_range rng; 
rng = v; 
rng = l; 

Il più grande svantaggio di tipo cancellazione è il costo di esecuzione - tutte le operazioni sono virtuali, e non possono essere inline (facilmente).


P.S. un altro famoso esempio di cancellazione del tipo è std::function

+0

Oltre a nascondere l'implementazione, il compilatore genera una singola definizione della funzione 'display_integers', anziché generare una versione per ogni tipo di iteratore che sia mai stata utilizzata. Questo è chiamato * codice bloat *. –

+0

@ DavidRodríguez-dribeas, ho già detto questo: "nel primo caso display_integers verrà compilato solo una volta ...". Ma nota, in caso di cancellazione del tipo - diverse classi di "Implementazione" saranno generate quando passerai valori di tipi diversi, non è "libero". –

+0

Sì, volevo solo sottolineare che l'implicazione diretta sta riducendo il codice * template bloat * –

10

boost::any_range può essere utilizzato per restituire intervalli di funzioni. Immaginate il seguente esempio:

auto make_range(std::vector<int> v) -> decltype(???) 
{ 
    return v | filter([](int x){ return x % 2 == 0;}) 
     | transform([](int x){ return x * 2;}); 
} 

*: gcc non compila il sopra senza avvolgendolo in std::function, hower clang 3.2 opere passando direttamente lambda

E 'molto difficile sapere ciò che viene restituito da questa funzione Inoltre, non funzionano insieme, quindi non possiamo dedurre il tipo utilizzando decltype quando si passa solo un lambda.Una soluzione è quella di utilizzare boost::any_range come quella nel tuo esempio (Un'altra soluzione è quella di utilizzare std::function come ha sottolineato Evgeny Panasyuk nei commenti):

integer_range make_range(std::vector<int> v) 
{ 
    return v | filter([](int x){ return x % 2 == 0;}) 
     | transform([](int x){ return x * 2;}); 
} 

esempio Lavorare with gcc utilizzando std::function.

Esempio di lavoro with clang passare direttamente lambda.

+2

Penso che il problema possa essere risolto anche in un altro modo: Definendo una variabile lambda globale in un dettaglio o in uno spazio dei nomi nascosto, quindi utilizzare il decltype per quella variabile lambda. Ecco un esempio funzionante completo del tuo: http://liveworkspace.org/code/3hdxki$6 –

+1

@GaborMarton: Giusto, tieni presente che probabilmente non c'è più [non c'è più bisogno della funzione gratuita] (http: // liveworkspace.org/code/3hdxki$8). –

+0

In questo caso particolare (in risposta) - non c'è alcun problema con lambda, perché si usa std :: function. decltype (v | boost :: adattatori :: filtrato (std :: function {})). http://liveworkspace.org/code/czlAX$0 –

Problemi correlati