2012-01-28 10 views
15

Questo abituato a lavorare qualche settimana fa:C++ 11 della funzione constexpr passata in argomento di un template

template <typename T, T t> 
T   tfunc() 
{ 
    return t + 10; 
} 

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

int main() 
{ 
    std::cout << func(10) << std::endl; 
    return 0; 
} 

Ma ora g++ -std=c++0x dice:

main.cpp: In function ‘constexpr T func(T) [with T = int]’: 
main.cpp:29:25: instantiated from here 
main.cpp:24:24: error: no matching function for call to ‘tfunc()’ 
main.cpp:24:24: note: candidate is: 
main.cpp:16:14: note: template<class T, T t> T tfunc() 
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type] 

clang++ -std=c++11 dice che i parametri del modello di tfunc<T, t>() vengono ignorati perché non valido.

È un bug o una correzione?

PS:

g++ --version =>g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version =>clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

+0

FWIW, Clang 3.1 HEAD genera anche gli stessi errori. – Xeo

risposta

7

Il parametro t non è un'espressione costante. Quindi l'errore. Va anche notato che non può essere un'espressione costante.

È possibile passare l'espressione costante come argomento, ma all'interno della funzione, l'oggetto (il parametro) che contiene il valore, non è un'espressione costante.

Dal t non è un'espressione costante, non può essere utilizzato come modello argomento:

return tfunc<T, t>(); //the second argument must be a constant expression 

Forse, si desidera qualcosa di simile:

template <typename T, T t> 
T tfunc() 
{ 
    return t + 10; 
} 

template <typename T, T t> //<---- t became template argument! 
constexpr T func() 
{ 
    return tfunc<T, t>(); 
} 

#define FUNC(a) func<decltype(a),a>() 

int main() 
{ 
    std::cout << FUNC(10) << std::endl; 
} 

Ora dovrebbe funzionare: online demo

+0

Sono ancora confuso dalla frase: * un argomento di una funzione non può essere un'espressione const * –

+0

@ Mr.Anubis: Ecco perché ho spiegato quella parte nel prossimo paragrafo. L'hai letto? – Nawaz

+0

vuoi dire '..func (T t)' dentro 'func',' t' in non constexp, giusto? –

1

Sembra che dovrebbe dare un errore - non ha modo di sapendo che sei passato in un valore costante come t a func.

Più in generale, non è possibile utilizzare i valori di runtime come argomenti del modello. I modelli sono intrinsecamente un costrutto in fase di compilazione.

+2

E le funzioni 'constexpr' possono essere valutate in fase di compilazione. – Xeo

+3

@Xeo Non vedo la pertinenza del tuo commento a questa risposta. Sembra, dico "La mia mano non può essere usata per camminare". e tu dici "E il tuo braccio può essere usato per muovere la mano". –

+0

rofl on analogy: D –

2

Ho l'impressione che constexpr debba anche essere valido in un contesto "runtime", non solo in fase di compilazione. Contrassegnare una funzione come constexpr incoraggia il compilatore a provare a valutarlo in fase di compilazione, ma la funzione deve comunque disporre di un'implementazione valida in fase di esecuzione.

In pratica, ciò significa che il compilatore non sa come implementare questa funzione in fase di esecuzione:

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

Una soluzione consiste nel modificare il costruttore tale che prende il parametro t come parametro normale, non come un parametro di template, e segnare il costruttore come constexpr:

template <typename T> 
constexpr T  tfunc(T t) 
{ 
    return t + 10; 
} 
template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T>(t); 
} 

ci sono tre livelli di 'espressione-costante-ness':

  1. parametro template int o (non-VLA) dimensione dell'array // Qualcosa che deve essere una costante espressione
  2. constexpr // Qualcosa che può essere una costante espressione
  3. non costante -expression

Non è possibile convertire gli elementi che sono in basso in quella lista in qualcosa che è alto in quella lista, ma ovviamente l'altro percorso è possibile.

Ad esempio, una chiamata a questa funzione

constexpr int foo(int x) { return x+1; } 

non è necessariamente una costante-espressione.

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why 
int array[foo(3)]; // this is OK 
int c = getchar(); 
int array[foo(c)]; // this will not compile (without VLAs) 

Quindi il valore restituito da una funzione constexpr è un'espressione costante soltanto se tutti i parametri, e l'implementazione della funzione, può essere completato in esecuzione al momento della compilazione.

+0

'constexpr int i = something_that_MUST_be_a_constexpr;' – Xeo

+0

@Xeo, deve * almeno * essere un 'constexpr'. Considerando che 'template struct X; X x; 'richiede che' i' sia anche più di un 'constexpr'. –

+0

Forse non avrei dovuto usare il secondo 'constexpr' come una scorciatoia per' espressione costante' ... :) Volevo solo dire che per 'constexpr', a volte qualcosa * deve * essere un'espressione costante - quando una variabile è dichiarata come tale. (Come risposta al tuo "constexpr' // Qualcosa che * potrebbe * essere un'espressione-costante".) – Xeo

2

Riassociare la domanda: si hanno due funzioni che prendono un parametro di tipo T. Uno prende il suo parametro come parametro template e l'altro come parametro 'normale'. Ho intenzione di chiamare le due funzioni funcT e funcN anziché tfunc e func. Si desidera essere in grado di chiamare funcT da funcN. Contrassegnare quest'ultimo come constexpr non aiuta.

Qualsiasi funzione contrassegnata come constexpr deve essere compilabile come se lo constexpr non fosse presente. Le funzioni constexpr sono un po 'schizofreniche. Si diplomano in espressioni costanti in determinate circostanze.

Non sarebbe possibile attuare funcN per funzionare a tempo di esecuzione in modo semplice, come sarebbe bisogno di essere in grado di funzionare per tutte possibili valori di t . Ciò richiederebbe al compilatore di istanziare molte istanze di tfunc, una per ogni valore di t. Ma puoi ovviare a questo se sei disposto a vivere con un piccolo sottoinsieme di T.C'è un limite modello-ricorsione di 1024 in g ++, in modo da poter facilmente gestire 1024 valori di T con questo codice:

#include<iostream> 
#include<functional> 
#include<array> 
using namespace std; 

template <typename T, T t> 
constexpr T funcT() { 
     return t + 10; 
} 

template<typename T, T u> 
constexpr T worker (T t) { 
     return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1); 

} 
template<> 
constexpr int worker<int,1000> (int) { 
      return -1; 
} 


template <typename T> 
constexpr T  funcN(T t) 
{ 
     return t<1000 ? worker<T,0>(t) : -1; 
} 

int main() 
{ 
    std::cout << funcN(10) << std::endl; 
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression 
    return 0; 
} 

Si utilizza una funzione worker che ricorsivamente convertire il 'normale' parametro t in un modello parametro u, che viene quindi utilizzato per istanziare ed eseguire tfunc<T,u>.

La linea cruciale è return funcT<T,u>() : worker<T, u+1>(t-1);

Questo ha limitazioni. Se si desidera utilizzare long o altri tipi integrali, è necessario aggiungere un'altra specializzazione. Ovviamente, questo codice funziona solo per t tra 0 e 1000 - il limite superiore esatto è probabilmente dipendente dal compilatore. Un'altra opzione potrebbe essere quella di utilizzare una ricerca binaria di sorta, con una funzione di operatore diverso per ogni potenza di 2:

template<typename T, T u> 
constexpr T worker4096 (T t) { 
     return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t); 

} 

Penso che questo sarà aggirare il modello-ricorsione limite, ma sarà ancora bisogno di molto gran numero di istanziazioni e renderebbe la compilazione molto lenta, se funziona a tutti.

Problemi correlati