2012-06-18 15 views
10

Sto lavorando con una libreria che espone un'interfaccia con cui lavorare. Una delle funzioni di questa libreria è come questo:Passare una variabile come argomento modello

template <int a> 
void modify(){} 

devo modificare i parametri da 1 a 10 cioè chiamata modify con con argomenti di template da 1 a 10. Per questo ho scritto questo codice (una versione di base di codice, il codice reale è molto più grande).

for(int i=0; i<10; i++){ 
    modify<i>(); 
} 

Sulla compilation ricevo il seguente errore

error: 'i' cannot appear in constant-expression 

Dopo aver attraversato alcuni link su internet, sono venuto a sapere che non posso passare qualsiasi valore come argomento di un template che non viene valutata al momento della compilazione . La mia domanda è la seguente: 1. Perché il compilatore non può valutare i in fase di compilazione? 2. C'è altro per raggiungere l'obiettivo che sto cercando di ottenere senza modificare l'interfaccia API?


C'è un'altra cosa che voglio fare. Chiama modifica come modifica dove VAR è l'output di alcuni calcoli funzionali. Come lo posso fare?

risposta

20

Qual è il valore di i (che non è una costante) in fase di compilazione? Non c'è modo di anser a meno che non si esegua il ciclo. Ma l'esecuzione non sta "compilando" Dato che non c'è risposta, il compilatore non può farlo.

I modelli non sono algoritmi da eseguire, ma macro da espandere per produrre codice. Che cosa si può fare è fare affidamento sulla specializzazione per implementare l'iterazione con la ricorsione, come qui:

#include <iostream> 

template<int i> 
void modify() 
{ std::cout << "modify<"<<i<<">"<< std::endl; } 

template<int x, int to> 
struct static_for 
{ 
    void operator()() 
    { modify<x>(); static_for<x+1,to>()(); } 
}; 

template<int to> 
struct static_for<to,to> 
{ 
    void operator()() 
    {} 
}; 


int main() 
{ 
    static_for<0,10>()(); 
} 

Si noti che, così facendo, si è, infatti, istanziare 10 funzioni denominate modificare < 0> ... modificare 9>, chiamato rispettivamente da static_for < 0,10> :: operator() ... static_for < 9,10> :: operator().

L'iterazione termina perché static_for < 10,10> verrà istanziato dalla specializzazione che prende due valori identici, che non fa nulla.

+0

+1. Stavo per pubblicare quasi la stessa risposta. – Nawaz

+0

@Emilio C'è un modo per chiamare modificare dove VAR è una variabile restituita da qualche calcolo funzionale, diciamo VAR = f()? – gibraltar

+0

@gibraltar: Solo se è C++ 11 e la funzione è contrassegnata come 'constexpr' (allora puoi semplicemente scrivere' modify '). E nota che le regole per constexpr sono piuttosto severe. –

2
  1. "Perché non si può valutare compilatore i al momento della compilazione?"

    Ciò vanificherebbe lo scopo dei modelli. I modelli sono lì per il caso in cui il codice sorgente ha lo stesso aspetto per alcuni set di casi, ma le istruzioni che il compilatore deve generare sono diverse ogni volta.

  2. "C'è dell'altro per raggiungere l'obiettivo che sto cercando di ottenere senza modificare l'interfaccia API?"

    Sì, guarda Boost.MPL.

    Tuttavia, sospetto che la risposta corretta qui sia che si desidera modificare l'API. Dipende dalla parte interna della funzione modify. So che hai la fonte, perché i modelli devono essere definiti nelle intestazioni.Quindi date un'occhiata perché ha bisogno di sapere i in fase di compilazione e se così non fosse, sarebbe meglio sostituire (o integrare se è necessario mantenere la retrocompatibilità) con la normale funzione con parametro.

+0

Puoi approfondire l'uso di Boost.MPL per favore? Non ho familiarità con questa libreria. – gibraltar

+0

@ gibraltar: Nemmeno io; leggi la documentazione. –

2

Poiché hai chiesto una risposta utilizzando Boost.MPL:

#include <boost/mpl/for_each.hpp> 
#include <boost/mpl/range_c.hpp> 

#include <iostream> 

template <int N> 
void modify() 
{ 
    std::cout << N << '\n'; 
} 

// You need to wrap your function template in a non-template functor 
struct modify_t 
{ 
    template <typename N> 
    void operator()(N) 
    { 
     modify<N::value>(); 
    } 
}; 

int main() 
{ 
    namespace mpl = boost::mpl; 

    mpl::for_each< mpl::range_c<int,0,10> >(modify_t()); // prints 0 to 9 
} 
1

Senza usare struct o Boost può anche essere fatto:

#include <iostream> 
#include <utility> 

template <int a> 
void modify() 
{ 
    std::cout<<a<<","; 
} 

template<int i,size_t... t> 
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>) 
{ 
    bool kai[]= { (modify<i+t>(), false)...}; 
} 

template<int i,int n> 
constexpr inline void CT_for() 
{ 
    CT_for_impl<i>(std::make_index_sequence<n-i+1>()); 
} 

int main() 
{ 
    CT_for<-5,5>(); 
    return 0; 
} 
1

soluzione all'errore: 'i' non può apparire in espressione costante per il problema precedente

To read about constexpr click this link

#include <iostream> 
using namespace std; 

template <typename T> 
void modify(T a) 
{ 
    cout<<a<<endl; //to check if its working 
} 


//func converts int a into const int a 
constexpr int func(int a) 
{ 
    return a; 
} 

int main(){ 
    for(int i=0; i<10; i++){ 
     modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr  
} 
    return 0; 
} 
+0

Questo non è ciò che l'OP intendeva. Innanzitutto, fare in modo che un intero di runtime constexpr y semplicemente passarlo a una funzione corrispondente non funzioni ... semplicemente non può. Ma anche se lo fosse, dovresti chiamare 'modify ', non le versioni non di tipo richieste dall'OP. – davidhigh

+0

Ma il modello verrà rilevato automaticamente – SSD

0

Dato che si desidera chiamare le funzioni in fase di esecuzione da loro indice e non è possibile modificare l'API, si può considerare di tipo cancellazione:

std::vector<std::function<void(int)> > func; 
func.push_back(modify<1>); 
func.push_back(modify<2>); 
//... and so on ... 
func.push_back(modify<10>); 

for(int i=0; i<10; ++i) 
{ 
    func[i](); //calls modify<i+1>(); 
} 

Alcuni punti da menzionare:

  • Questo non è ciò che i modelli sono principalmente per, ma è un modo per portare come biblioteca tatic al mondo run-time. Il requisito di base per questo è che si lavori con tipi omogenei (--if modify<7>() restituirebbe, ad esempio, un std::string l'intero approccio si interromperebbe).
  • La soluzione precedente che utilizza la cancellazione del tipo ha un sovraccarico. Si può forse ottenere più velocemente usando i puntatori di funzione, ma sarà sempre più lento di chiamare le funzioni in fase di compilazione.
  • Uno può (e dovrebbe) anche avvolgere i push_back in un'altra funzione statica iterativa per evitare le chiamate manuali.
Problemi correlati