Sto sperimentando con le funzioni di constexpr in C++ 14. Il codice seguente, che calcola le opere fattoriali come previsto:C++ 14: tipi di ritorno (auto) dedotti da constexpr con espressioni ternarie
template <typename T>
constexpr auto fact(T a) {
if(a==1)
return 1;
return a*fact(a-1);
}
int main(void) {
static_assert(fact(3)==6, "fact doesn't work");
}
quando viene compilato come segue con clang:
> clang++ --version
clang version 3.5.0 (tags/RELEASE_350/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
> clang++ -std=c++14 constexpr.cpp
Tuttavia, quando cambio la definizione fact
di utilizzare la ?
operatore ternario:
template <typename T>
constexpr auto fact(T a) {
return a==1 ? 1 : a*fact(a-1);
}
ottengo il seguente errore del compilatore:
> clang++ -std=c++14 constexpr.cpp
constexpr.cpp:12:31: fatal error: recursive template instantiation exceeded maximum depth of
256
return a==T(1) ? T(1) : a*fact(a-1);
... snip ...
constexpr.cpp:16:19: note: in instantiation of function template specialization 'fact<int>'
requested here
static_assert(fact(3)==6, "fact doesn't work");
Il problema è stato risolto se affermo in modo esplicito il tipo di ritorno T (invece di utilizzare auto per dedurre il tipo di ritorno)
template <typename T>
constexpr T fact(T a) {
return a==1 ? 1 : a*fact(a-1);
}
Se rimuovo il parametro del modello, lo schema si ripete (la versione ternario non riesce, e la versione if
funziona)
// this works just fine
constexpr auto fact(int a) {
if(a==1)
return 1;
return a*fact(a-1);
}
che tale non riesce
constexpr auto fact(int a) {
return a==1 ? 1 : a*fact(a-1);
}
con il seguente errore
> clang++ -std=c++14 constexpr.cpp
constexpr.cpp:16:25: error: function 'fact' with deduced return type cannot be used before it
is defined
return a==1 ? 1 : a*fact(a-1);
^
constexpr.cpp:15:16: note: 'fact' declared here
constexpr auto fact(int a) {
^
constexpr.cpp:20:26: error: invalid operands to binary expression ('void' and 'int')
static_assert(fact(3)==6, "fact doesn't work");
~~~~~~~^ ~
2 errors generated.
cosa sta succedendo qui?
la versione se ha due istruzioni return. Il compilatore usa/standard specifica una specie di evaulazione pigra, dove considera solo l'istruzione return nel blocco if quando a == 1? Altrimenti avremmo lo stesso problema con entrambe le versioni. – bcumming
Grazie @Praetorian. Posso cercarlo da solo (anche se non sono un avvocato di lingua). – bcumming
@Praetorian Non è l'opposto di quello che hai affermato in precedenza. Il tipo di ritorno è dedotto per ogni dichiarazione di ritorno, ma una volta dedotto per * qualsiasi * dichiarazione di ritorno, il tipo di reso è noto e può essere utilizzato come parte della deduzione di dichiarazioni di reso successive. La ragione per cui il tuo esempio con 'template' fallisce perché il fatto che il tipo di reso 'sia noto non dice nulla sul fatto '' tipo di ritorno, ma è 'fact ' che chiama 'fact ' nel codice dell'OP . –
hvd