2015-11-15 9 views
6

Nel documento standard P0092R1, Howard Hinnant ha scritto:Come può questo codice essere constexpr? (Std :: crono)

template <class To, class Rep, class Period, 
      class = enable_if_t<detail::is_duration<To>{}>> 
constexpr 
To floor(const duration<Rep, Period>& d) 
{ 
    To t = duration_cast<To>(d); 
    if (t > d) 
     --t; 
    return t; 
} 

Come può questo codice lavoro? Il problema è che operator-- su un std::chrono::duration non è un'operazione di constexpr. Essa è definita come:

duration& operator--(); 

Eppure questo codice viene compilato, e dà la risposta giusta al momento della compilazione:

static_assert(floor<hours>(minutes{3}).count() == 0, "”); 

Cosa c'è in questo?

+0

FWIW, la versione originale (P0) del documento di Howard è disponibile qui: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0092r0.html –

risposta

9

La risposta è che non tutte le operazioni in una routine di compilazione devono essere constexpr; solo quelli che vengono eseguiti in fase di compilazione.

Nell'esempio precedente, le operazioni sono:

hours t = duration_cast<hours>(d); 
if (t > d) {} // which is false, so execution skips the block 
return t; 

ognuno dei quali può essere fatto al momento della compilazione.

Se, d'altra parte, si dovesse provare:

static_assert(floor<hours>(minutes{-3}).count() == -1, "”); 

darebbe un errore di compilazione dicendo (utilizzando clang):

error: static_assert expression is not an integral constant expression 
     static_assert(floor<hours>(minutes{-3}).count() == -1, ""); 
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
note: non-constexpr function 'operator--' cannot be used in a constant expression 
         --t; 
         ^
note: in call to 'floor(minutes{-3})' 
     static_assert(floor<hours>(minutes{-3}).count() == -1, ""); 

Quando si scrive il codice constexpr, è prendere in considerazione tutti i percorsi attraverso il codice.

P.S. È possibile correggere la proposta di floor routine di questa convenzione:

template <class To, class Rep, class Period, 
      class = enable_if_t<detail::is_duration<To>{}>> 
constexpr 
To floor(const duration<Rep, Period>& d) 
{ 
    To t = duration_cast<To>(d); 
    if (t > d) 
     t = t - To{1}; 
    return t; 
} 
0

Secondo le norme di n3597 e n3652, espressioni all'interno di una funzione constexpr non si devono essere espressioni costanti, a patto che non modificano lo stato a livello globale visibile.

C'è un esempio di

constexpr int f(int a) { 
    int n = a; 
    ++n;     // '++n' is not a constant expression 
    return n * a; 
} 
int k = f(4);   // OK, this is a constant expression. 
         // 'n' in 'f' can be modified because its lifetime 
         // began during the evaluation of the expression. 

Molto probabilmente queste sono le regole che Howard Hinnant rispettate quando si scrive la carta si parla.

Per lavorare con il codice duration<T> nella domanda, operator-- dovrebbe essere effettuata una funzione constexpr. Dal momento che le modifiche alla libreria constexpr non erano definitive, è facile capire come Howard avrebbe potuto fare affidamento su un tale cambiamento.

+2

Sorry Ben, Marshall ha questo esattamente giusto. Gli articoli che hai citato sono in C++ 14 e ho fatto questo errore molto recentemente, usando C++ 14. –