Perché è necessario contrassegnare le funzioni come constexpr?

Perché è necessario contrassegnare le funzioni come constexpr?

Impedire al codice client di aspettarsi più di quanto prometti

Supponiamo che sto scrivendo una libreria e che ci sia una funzione che attualmente restituisce una costante:

awesome_lib.hpp :

inline int f() { return 4; }

Se constexpr non era richiesto, tu, come autore del codice client, potresti andare via e fare qualcosa del genere:

client_app.cpp :

#include <awesome_lib.hpp>
#include <array>

std::array<int, f()> my_array;   // needs CT template arg
int my_c_array[f()];             // needs CT array dimension

Quindi dovrei cambiare f() per dire restituire il valore da un file di configurazione, il tuo codice client si interromperebbe, ma non avrei idea di aver rischiato di rompere il tuo codice. In effetti, potrebbe essere solo quando hai qualche problema di produzione e vai a ricompilare che trovi questo problema aggiuntivo frustrante la tua ricostruzione.

Modificando solo l'implementazione di f() , avrei effettivamente modificato l'utilizzo che si poteva fare dell'interfaccia .

Invece, C++11 in poi fornisce constexpr quindi posso denotare che il codice client può avere una ragionevole aspettativa che la funzione rimanga un constexpr , e usalo come tale. Sono a conoscenza e approvo tale utilizzo come parte della mia interfaccia. Proprio come in C++03, il compilatore continua a garantire che il codice client non sia costruito per dipendere da altri non constexpr funzioni per prevenire lo scenario di "dipendenza indesiderata/sconosciuta" di cui sopra; è più della semplice documentazione:è l'applicazione del tempo di compilazione.

È interessante notare che ciò continua la tendenza C++ di offrire alternative migliori per gli usi tradizionali delle macro del preprocessore (considerare #define F 4 e come il programmatore client sa se il programmatore lib considera un gioco equo cambiare per dire #define F config["f"] ), con i loro noti "mali" come essere al di fuori del sistema di ambito dei nomi/classe della lingua.

Perché non c'è una diagnostica per le funzioni "ovviamente" mai const?

Penso che la confusione qui sia dovuta a constexpr non assicurando in modo proattivo che ci sia una serie di argomenti per i quali il risultato sia effettivamente const in fase di compilazione:piuttosto, richiede al programmatore di assumersene la responsabilità (altrimenti §7.1.5/5 nello Standard ritiene che il programma sia mal formato ma non 'non richiedere al compilatore di emettere una diagnostica). Sì, è un peccato, ma non rimuove l'utilità sopra di constexpr .

Quindi, forse è utile passare dalla domanda "qual è lo scopo di constexpr " considerare "perché posso compilare un constexpr funzione che non può mai effettivamente restituire un valore const?" .

Risposta:perché ci sarebbe bisogno di un'analisi di ramo esauriente che potrebbe coinvolgere un numero qualsiasi di combinazioni. Potrebbe essere eccessivamente costoso in tempo di compilazione e/o memoria, anche al di là della capacità di qualsiasi hardware immaginabile, da diagnosticare. Inoltre, anche quando è pratico, dover diagnosticare tali casi in modo accurato è un'intera nuova scatola di worm per gli scrittori di compilatori (che hanno usi migliori per il loro tempo). Ci sarebbero anche implicazioni per il programma come la definizione di funzioni richiamate dall'interno del constexpr funzione che deve essere visibile quando è stata eseguita la convalida (e funzioni che la funzione chiama ecc.).

Nel frattempo, mancano di constexpr continua a vietare l'uso come valore const:il rigore è sul sans-constexpr lato. È utile come illustrato sopra.

Confronto con funzioni membro non `const`

  • constexpr impedisce int x[f()] mentre la mancanza di const impedisce const X x; x.f(); - entrambi assicurano che il codice client non inserisca in hardcode dipendenze indesiderate

  • in entrambi i casi, non vorresti che il compilatore determini const[expr] -ness automaticamente :

    • non vorresti che il codice client chiamasse una funzione membro su un const oggetto quando puoi già anticipare che la funzione si evolverà per modificare il valore osservabile, rompendo il codice client

    • non vorrai che un valore venga utilizzato come parametro del modello o dimensione dell'array se hai già previsto che venga determinato in seguito in fase di esecuzione

  • differiscono in quanto il compilatore applica const uso di altri membri all'interno di un const funzione membro, ma non applica un risultato costante in fase di compilazione con constexpr (a causa delle limitazioni pratiche del compilatore)


Quando ho insistito su Richard Smith, un autore di Clang, ha spiegato:

All'inizio tutto ciò non sembrava convincente, ma se si analizzano i dettagli, le cose si svelano senza constexpr . Non è necessario creare un'istanza di una funzione finché non viene utilizzata da ODR, il che significa essenzialmente utilizzata in fase di esecuzione. Cosa rende speciale constexpr funzioni è che possono violare questa regola e richiedere comunque un'istanza.

L'istanza di funzione è una procedura ricorsiva. L'istanziazione di una funzione comporta l'istanziazione delle funzioni e delle classi che utilizza, indipendentemente dagli argomenti di una chiamata particolare.

Se qualcosa è andato storto durante l'istanziazione di questo albero delle dipendenze (potenzialmente a spese significative), sarebbe difficile ingoiare l'errore. Inoltre, l'istanza del modello di classe può avere effetti collaterali di runtime.

Data una chiamata di funzione in fase di compilazione dipendente dall'argomento in una firma di funzione, la risoluzione dell'overload può comportare l'istanziazione di definizioni di funzioni semplicemente ausiliarie a quelle nel set di overload, comprese le funzioni che non vengono nemmeno chiamate. Tali istanze possono avere effetti collaterali tra cui malformazioni e comportamenti in fase di esecuzione.

È un caso d'angolo per essere sicuri, ma possono succedere cose brutte se non richiedi alle persone di aderire a constexpr funzioni.


Senza la parola chiave, il compilatore non può diagnosticare gli errori. Il compilatore non sarebbe in grado di dirti che la funzione non è sintatticamente valida come constexpr . Sebbene tu abbia affermato che ciò fornisce un "falso senso di sicurezza", credo sia meglio raccogliere questi errori il prima possibile.