2012-10-23 11 views
15

Normalmente, constexpr deve essere privo di effetti collaterali. Tuttavia, ho appena scoperto che è possibile utilizzare gli effetti collaterali nei costruttori di eccezioni generate. Questa tecnica può essere utilizzata per emulare assert() per le funzioni di constexpr, come è dimostrato nel seguente programma.È legale utilizzare gli effetti collaterali nelle eccezioni generate da constexpr?

#include <iostream> 
#include <cstdlib> 
#include <stdexcept> 

struct constexpr_precond_violated : std::logic_error 
{ 
    constexpr_precond_violated(const char* msg) : 
    std::logic_error(msg) 
    { 
    std::cerr << msg << '\n'; 
    abort(); // to get a core dump 
    } 
}; 

#define TO_STRING_IMPL(x) #x 
#define TO_STRING(x) TO_STRING_IMPL(x) 

#define CONSTEXPR_PRECOND(cond, value) \ 
    ((!(cond)) ? throw constexpr_precond_violated(\ 
    "assertion: <" #cond "> failed (file: " \ 
    __FILE__ ", line: " TO_STRING(__LINE__) ")") \ 
    : (value)) 

constexpr int divide(int x, int y) 
{ 
    return CONSTEXPR_PRECOND(y != 0, x/y); 
} 

int main(int argc, char** argv) 
{ 
    // The compiler cannot know argc, so it must be evaluated at runtime. 
    // If argc is 2, the precondition is violated. 
    return divide(100, argc - 2); 
} 

L'ho provato con g ++ 4.7.2 e clang ++ 3.1. Quando le precondizioni falliscono, si ottiene il percorso dell'errore e un core dump.

./constexpr_assert some_arg 
assertion: <y != 0> failed (file: constexpr_assert.cpp, line: 26) 
Aborted (core dumped) 

Quindi funziona con i compilatori correnti, ma è legale C++ 11?

+1

Fuori interesse, cosa succede se si fornisce una costante in tempo di compilazione '0' come secondo argomento a' divide() '? Il * compilatore * "genera un'eccezione"? :) –

+0

static_assert (divide (1, 0)> = 0, "testing"); semplicemente non compila divisione di restituzione (1, 0); compila con Clang e non riesce solo in fase di esecuzione. –

+3

È necessario fare attenzione a distinguere tra espressioni costanti e una funzione 'constexpr'. Qualcosa come "constexpr" deve essere privo di effetti collaterali "è impreciso. Pensa a 'constexpr' come non più di una parola chiave (viene in mente' static'). –

risposta

14

È legale.

Per ogni funzione constexpr ci devono essere alcuni valori di argomento che risultano in una costante espressione (§7.1.5/5):

Per una funzione constexpr, se nessun valore funzione degli argomenti esistono come che la la sostituzione della funzione invocazione produrrebbe un'espressione costante (5.19), il programma è mal formato; nessuna diagnostica richiesta.

noti che questo non significa che ogni possibile valore argomento deve comportare un'espressione costante. divide ha chiaramente alcuni valori di argomento che risultano in un'espressione costante: divide(1, 1) è un semplice esempio. Quindi, la definizione è chiaramente valida.

Ma è possibile chiamare divide(1, 0)? Sì, può. Non c'è quasi nessuna differenza tra chiamando una funzione constexpr o una funzione "normale" (§7.1.5/7):

Una chiamata a una funzione constexpr produce lo stesso risultato di una chiamata a un equivalente non constexpr funzione sotto tutti gli aspetti tranne che una chiamata a una funzione constexpr può apparire in un'espressione costante.

Nota che le chiamate a funzioni constexprpuò apparire in espressioni costanti, ma nulla vieta loro di non conseguente espressioni costanti. Questo è inteso in modo tale che è possibile chiamare le funzioni constexpr con argomenti sia in fase di compilazione che in fase di esecuzione (altrimenti l'utilità di constexpr potrebbe essere limitata in molti modi).

Per completezza, vediamo cosa rende un'espressione costante (§5.19/2):

A condizionale espressione- è un nucleo costante espressione meno che comporta una delle seguenti come sottoespressione potenzialmente valutata (§3.2), ma le sottoespressioni delle operazioni logiche AND (§5.14), logiche OR (§5.15), e condizionale (§5.16) che non sono valutate non sono considerate considerate [...].

Quindi, divide(1, 1) è un'espressione costante, ma non lo è divide(1, 0).Se hai usato divide(1, 0) in un parametro template, il programma sarebbe mal formato. Ma per il resto va bene.