TL; DR:
Per clang si desidera l'argomento della riga di comando -fconstexpr-steps=1271242
e non avete bisogno di più di -fconstexpr-depth=27
Il metodo ricorsivo di calcolo I numeri di Fibonacci non richiedono molta profondità di ricorsione. La profondità richiesta per fib(n)
non è in realtà più di n
. Questo perché la più lunga catena di chiamate è attraverso la chiamata ricorsiva fib(i-1)
.
constexpr auto fib_1 = fib_cxpr(3); // fails with -fconstexpr-depth=2, works with -fconstexpr-depth=3
constexpr auto fib_1 = fib_cxpr(4); // fails with -fconstexpr-depth=3, works with -fconstexpr-depth=4
Quindi possiamo concludere che -fconstexpr-depth
non è l'impostazione che conta.
Inoltre, i messaggi di errore indicano anche una differenza:
constexpr auto fib_1 = fib_cxpr(27);
Compilato con -fconstexpr-depth=26
, per essere sicuri ci ha colpito quel limite, clang genera il messaggio:
note: constexpr evaluation exceeded maximum depth of 26 calls
Ma compilazione con -fconstexpr-depth=27
, che è abbastanza profondità, produce il messaggio:
note: constexpr evaluation hit maximum step limit; possible infinite loop?
Quindi sappiamo che clang distingue tra due errori: profondità di ricorsione e "limite di passo".
I primi risultati di Google per 'limite massimo passo clang' portare alla consultabili per il cerotto clangore di attuazione questa funzionalità, compresa l'attuazione dell'opzione della riga di comando: -fconstexpr-steps
. Googling di questa opzione indica che non esiste documentazione a livello utente.
Quindi non c'è documentazione su ciò che conta il clang come un "passo" o quanti clang "passaggi" richiede per fib(27)
. Potremmo semplicemente impostarlo molto in alto, ma penso che sia una cattiva idea. Invece qualche sperimentazione mostra:
n : steps
0 : 2
1 : 2
2 : 6
3 : 10
4 : 18
che indica che passi (fib(n)
) == gradini (fib(n-1)
) + passi (fib(n-2)
) + 2. Un po 'di calcolo mostra che, in base a questo, fib(27)
dovrebbe richiedere 1.271.242 di Clang di passi. Quindi compilare con -fconstexpr-steps=1271242
dovrebbe consentire la compilazione del programma, che in effetti è it does.La compilazione con -fconstexpr-steps=1271241
produce un errore come prima, quindi sappiamo che abbiamo un limite esatto.
Un'alternativa, meno esatto metodo comporta l'osservare dal cerotto che il limite passo predefinito è 1.048.576 (2), che è ovviamente sufficiente per fib(26)
. Intuitivamente, il raddoppio dovrebbe essere abbondante, e dalla precedente analisi sappiamo che due milioni sono abbondanti. Un limite stretto sarebbe ⌈φ · passi (fib(26)
) ⌉ (che corrisponde esattamente a 1.271.242).
Un'altra cosa da notare è che questi risultati mostrano chiaramente che clang non sta facendo alcuna memoizzazione della valutazione di constexpr. GCC does, ma sembra che questo non sia implementato in clang. Sebbene la memoizzazione aumenti i requisiti di memoria, a volte può, come in questo caso, ridurre notevolmente il tempo necessario per la valutazione. Le due conclusioni che traggo da questo sono che scrivere codice constexpr che richiede la memoizzazione per buoni tempi di compilazione non è una buona idea per il codice portatile, e che clang potrebbe essere migliorato con il supporto per la memoization di constexpr e un'opzione della riga di comando per abilitarlo/disabilitarlo.
@Brian: grazie. In qualche modo non ero in grado di formattarlo correttamente. – Sarang
Hm, GCC lo gestisce bene ... –
D'altra parte, Clang è perfettamente soddisfatto di un tradizionale metaprogramma in stile modello. –