2014-09-17 15 views
37

Mi chiedevo se è possibile scrivere una funzione modello che può assumere qualsiasi altro modello arbitrario come parametro e corrispondere correttamente al nome del modello (ad esempio, non solo la classe risultante). Quello che so al lavoro è questo:Argomento modello template variadic C++ che corrisponde a qualsiasi tipo di parametro

template<template<typename ...> class TemplateT, typename... TemplateP> 
void f(const TemplateT<TemplateP...>& param); 

che corrisponderà ad esempio per f(std::vector<int>()) o f(std::list<int>()) ma non funziona per f(std::array<int, 3>()), come secondo parametro è un size_t e nessun tipo.

ora penso si possa fare qualcosa di pazzo come:

template<template<typename ...> class TemplateT, size... Sizes, typename... TemplateP> 
void f(const TemplateT<Sizes..., TemplateP...>& param); 

Sperando che il compilatore deriverebbe correttamente sia i puntini di sospensione TemplateP o Sizes puntini di sospensione per essere vuoto. Ma non solo è brutto, ma funzionerà anche solo per i modelli che accettano entrambi i tipi o i parametri size_t. Ancora non abbinerà i modelli arbitrari per esempio con i parametri bool.

Lo stesso vale per un approccio con sovraccarico:

template<template<typename ...> class TemplateT, typename... TemplateP> 
void f(const TemplateT<TemplateP...>& param); 

template<template<typename ...> class TemplateT, size... Sizes> 
void f(const TemplateT<Sizes...>& param); 

Inoltre, tale approccio abituato' lavoro se vogliamo mescolare size_t e typenames. Allora, cosa sarebbe necessario per soddisfare qualsiasi cosa sarebbe qualcosa come questo, dove non ci sono vincoli a tutti per ciò che è consentito nel puntini di sospensione:

template<template<...> class TemplateT, ... Anything> 
void f(const TemplateT<Anything...>& param); 

Ciò sintassi non funziona, ma forse c'è altra sintassi per definire qualcosa come questo?

Questo principalmente mi chiedo che cosa è possibile nella lingua, ho pensato che potrebbe esserci effettivamente un utilizzo per esso, se si dispone di modelli diversi in cui il primo parametro è sempre fisso e si vorrebbe cambiarlo in base al tipo di ritorno e tieni tutto il resto. Qualcosa del genere:

Quindi, un modo per rendere questo lavoro in modo completamente generico utilizzando la corrispondenza dei modelli?

Questo non è puramente un esperimento mentale, come il caso d'uso per questo, dove sono rimasto bloccato era quello di creare primitive funzionali puri che operano su contenitori e sarà implicitamente costruire contenitori risultato immutabili. Se il contenitore dei risultati ha un tipo di dati diverso, è necessario conoscere il tipo su cui opera il contenitore, quindi l'unico requisito su qualsiasi contenitore è che il primo parametro del modello deve essere il tipo di input in modo che possa essere sostituito con un altro il tipo di output nel risultato, ma il codice dovrebbe essere ignaro di qualsiasi argomento del modello che verrà dopo e non dovrebbe importare se si tratti di un tipo o di un valore.

+9

Non c'è modo di abbinare anche * un * parametro modello non di tipo se non si conosce il suo tipo. Temo che tu stia chiedendo troppe cose troppo buone. –

+10

@ n.m. In altre parole, gli argomenti value sono argomenti template di seconda classe. (Usa un wrapper ('std :: integral_constant') per evitare gli svantaggi. (Dovrebbero aver implementato l'auto-wrapping lì ...) – Deduplicator

+1

@nm: D'accordo, non è già possibile per i parametri 'normali'. Sono stati aggiunti parametri variadici per poter specificare più modelli generici, quindi c'è qualche speranza che qualcosa di simile sia stato aggiunto/sarà aggiunto –

risposta

0

È necessario disporre di una metafunzione che ricollega il tipo di contenitore. perché non si può semplicemente sostituire primo parametro modello:

vector<int, allocator<int> > input; 
vector<double, allocator<int> > just_replaced; 
vector<double, allocator<double> > properly_rebound; 

Quindi, basta scrivere un tale metafunction per serie nota di contenitori.

template<class Container, class NewValue> class rebinder; 

// example for vectors with standard allocator 
template<class V, class T> class rebinder< std::vector<V>, T > { 
public: 
    typedef std::vector<T> type; 
}; 
// example for lists with arbitrary allocator 
template<class V, class A, class T> class rebinder< std::list<V,A>, T > { 
    typedef typename A::template rebind<T>::other AT; // rebind the allocator 
public: 
    typedef std::list<T,AT> type; // rebind the list 
}; 
// example for arrays 
template<class V, size_t N> class rebinder< std::array<V,N>, T > { 
public: 
    typedef std::array<T,N> type; 
}; 

Le regole di riavvolgimento possono variare a seconda del contenitore.

Inoltre si potrebbe richiedere un metafunction che estrae tipo di valore dal contenitore arbitrario, non solo STD-conforme (typedef *unspecified* value_type)

template<class Container> class get_value_type { 
public: 
    typedef typename Container::value_type type; // common implementation 
}; 
template<class X> class get_value_type< YourTrickyContainer<X> > { 
    ...... 
public: 
    typedef YZ type; 
}; 
+0

Sì, funzionerebbe per quel caso specifico di contenitori STl. Ma i contenitori Qt per esempio non hanno allocatori. Inoltre, stavo cercando qualcosa di più generico, i contenitori erano solo un esempio. –

3

tuo costrutto interessante ha due livelli con i modelli variadic.

  • Un modello variadic lista dei parametri esterna TemplateP & Sizes per un modello funzione
  • Un parametro pacchetto interno, come i parametri del modello del vostro modello di parametro di template TemplateT, un modello classe

Per prima cosa, diamo un'occhiata alla classe interna TemplateT: perché l'operatore ellittico non può corrispondere a qualcosa come TemplateT< int, 2 >? Beh, lo standard definisce i modelli variadic in §14.5.3 come

template<class ... Types> struct Tuple { }; 
template<T ...Values> struct Tuple2 { }; 

in cui il pacco modello argomento nel primo caso può solo tipi di corrispondenza e nella seconda versione solo valori di tipo T. In particolare,

Tuple <0> error; // error, 0 is not a type! 
Tuple < T, 0 > error2; // T is a type, but zero is not! 
Tuple2<T> error3; // error, T is not a value 
Tuple2< T, 0 > error4; // error, T is not a value 

sono tutti malformati. Inoltre, non è possibile ripiegare a qualcosa di simile

template<class ... Types, size_t ...Sizes> struct Tuple { }; 

perché gli stati standard in §14.1.11:

Se un modello-parametro di un modello di classe primaria o un modello alias è un template parameter pack, è l'ultimo parametro template. Un parametro parametrico modello di un modello funzione non deve essere seguito da un altro parametro modello a meno che il parametro modello non possa essere dedotto dall'elenco tipo-parametro del modello funzione o abbia un argomento predefinito (14.8.2).

In altre parole, per i modelli di classe solo uno parametro pacchetto variadic possono apparire nella definizione. Pertanto la sopra (doppia) definizione della classe variadica è malformata. Poiché la classe interiore ha sempre bisogno di una tale combinazione, è impossibile scrivere qualcosa di generale come hai concepito.


Cosa può essere salvato? Per il modello di funzione esterna, alcuni frammenti possono essere messi insieme, ma non ti piacerà. Finché il secondo pacchetto di parametri può essere dedotto dal primo, possono comparire due pacchetti di parametri (in un modello di funzione). Pertanto, una funzione come

template < typename... Args, size_t... N > void g(const std::array< Args, N > &...arr); 
g(std::array< double, 3 >(), std::array< int, 5>()); 

è consentita, poiché i valori interi possono essere dedotti. Naturalmente, questo dovrebbe essere specializzato per ogni tipo di contenitore ed è lontano da ciò che avevi immaginato.

+0

Hmm. Ho appena letto anche la sezione e in base a quella sezione int ... o qualcosa del genere non è nemmeno permesso a tutti, sono abbastanza sicuro tuttavia che clang almeno lo supporta. –

+0

Grazie per aver segnalato questo. Ho esaminato ulteriormente questo aspetto e penso che la risposta ci sia ora. – user1978011

0

Sarebbe fantastico se avessimo tale cosa, in quanto ci avrebbe permesso di scrivere un tratto is_same_template in un gioco da ragazzi.
Fino ad allora, ci siamo specializzati fino in fondo.