2016-02-08 22 views
5

Recentemente ho fatto una domanda qui (Detecting instance method constexpr with SFINAE) dove ho provato a fare qualche rilevamento di constexpr in fase di compilazione. Alla fine, ho capito che si può sfruttare noexcept per fare questo: qualsiasi espressione costante è anche noexcept. Così ho messo insieme i seguenti macchinari:Decrittografia Constex

template <class T> 
constexpr int maybe_noexcept(T && t) { return 0; } 
... 
constexpr bool b = noexcept(maybe_noexcept(int{})); 

Questo funziona e b è vero come ci si aspetterebbe, come zero-in fase di inizializzazione di un int è un'espressione costante. Rende correttamente anche zero quando dovrebbe (se cambio int in un altro tipo appropriato).

Successivamente, volevo controllare se qualcosa è constexpr spostare costruibile. Quindi ho fatto questo:

constexpr bool b = noexcept(maybe_noexcept(int(int{}))); 

E ancora una volta, questo funziona correttamente per int, o un tipo definito dall'utente. Tuttavia, questo controlla che il tipo abbia sia un costruttore constexpr predefinito sia un costruttore di movimento constexpr. Quindi, per ovviare a questo, ho provato a cambiare a declval:

constexpr bool b = noexcept(maybe_noexcept(int(declval<int>()))); 

Ciò si traduce in b essere falsa a gcc 5.3.0 (non è possibile utilizzare clang per niente di tutto questo, perché clang non correttamente fa costante espressioni noexcept). Nessun problema, dico, deve essere perché declval è (abbastanza interessante) non contrassegnato constexpr. Così scrivo la mia versione ingenua:

template <class T> 
constexpr T&& constexpr_declval() noexcept; 

Sì, questo è ingenuo rispetto a come la libreria standard lo fa in quanto sarà soffocare sul vuoto e probabilmente altre cose, ma va bene per ora. Così cerco di nuovo:

constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>()))); 

Questo non funziona ancora, b è sempre false. Perché questa non è considerata un'espressione costante? Si tratta di un bug del compilatore o non sto capendo di fondamentale su constexpr? Sembra che ci sia una strana interazione tra constexpr e contesti non valutati.

+1

@ Cameron La seconda parte di ciò che hai detto è certamente vera, ma il primo tecnicamente non lo è. Ogni espressione costante * è * '' noexcept'', non è che possa essere. Tuttavia, il ritorno di una funzione '' constexpr'' non è sempre un'espressione costante. –

+1

La chiamata di funzione non è un'espressione costante perché non è definita ([expr.const] /2.3) – 0x499602D2

risposta

6

constexpr le espressioni devono essere definite. Il tuo non è definito, quindi in tal caso int(constexpr_declval<int>()) non è constexpr.

Il che significa che maybe_noexcept(int(constexpr_declval<int>())) non è un constexpr, quindi non è noexcept.

E il compilatore restituisce correttamente false.

Non è possibile richiamare UB in un constexpr.

Non riesco a pensare a un modo per fare un riferimento constexpr a dati arbitrari. Stavo pensando a un buffer constexpr di memoria allineato reinterpretato come riferimento al tipo di dati, ma questo è UB in molti contesti, quindi non- constexpr.

In generale, questo non è possibile. Immagina di avere una classe il cui stato determina se la chiamata al metodo è constexpr:

struct bob { 
    int alice; 
    constexpr bob(int a=0):alice(a) {} 
    constexpr int get() const { 
    if (alice > 0) throw std::string("nope"); 
    return alice; 
    } 
}; 

ora, è bob::getconstexpr o no? È se si dispone di un constexpr bob costruito con un valore non positivo alice e ...non è se non

Non puoi dire "fai finta che questo valore sia constexpr e dimmi se qualche espressione è constexpr". Anche se fosse possibile, non risolverebbe il problema in generale, perché lo stato di un parametro constexpr può cambiare se un'espressione è constexpr oppure no!

Ancora più divertente, bob().get()è constexpr, mentre bob(1).get() non è. Quindi il tuo primo tentativo (predefinito costruisci il tipo) ha dato anche la risposta sbagliata: puoi testare, poi fare l'azione, e l'azione fallirà.

L'oggetto è effettivamente un parametro del metodo e, senza lo stato di tutti i parametri, non è possibile determinare se una funzione è constexpr.

Il modo per determinare se un'espressione è constexpr è di eseguirlo in un contesto constexpr e vedere se funziona.

+0

Quindi sembra proprio che sia effettivamente impossibile verificare correttamente se esistono metodi di istanza di constexpr per una classe, poiché è necessario ottenere un istanza per farlo, e non c'è modo di ottenere un'istanza di constexpr che sia effettivamente definita e non presupponga l'esistenza di qualche costruttore. Questa è una descrizione corretta della situazione? –

+1

@NirFriedman Bene, puoi passare argomenti per costruire detto oggetto. ;) In generale, 'constexpr' dipende dal contesto - è necessario conoscere quasi * tutto * sulla chiamata e i suoi parametri. Uno dei parametri è l'oggetto * che effettua la chiamata *: se non è 'constexpr' l'espressione è (in generale) no. Dire "se fingi che questo argomento sia' constexpr' anche se non è ciò che accade "non è supportato, per quanto ne so. – Yakk

+0

Haha davvero. Bene, la tua risposta + commento è stata molto istruttiva, grazie. –